2021-03-22 14:14:21 +00:00
///////////////////////////////////////////
// plic.sv
//
// Written: bbracker@hmc.edu 18 January 2021
// Modified:
//
// Purpose: Platform-Level Interrupt Controller
2021-04-04 10:40:53 +00:00
// Based on RISC-V spec (https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc)
// With clarifications from ROA's existing implementation (https://roalogic.github.io/plic/docs/AHB-Lite_PLIC_Datasheet.pdf)
// Supports only 1 target core and only a global threshold.
2021-03-22 14:14:21 +00:00
//
2021-04-04 10:40:53 +00:00
// *** Big questions:
// Do we detect requests as level-triggered or edge-trigged?
// If edge-triggered, do we want to allow 1 source to be able to make a number of repeated requests?
//
2021-03-22 14:14:21 +00:00
// A component of the Wally configurable RISC-V project.
//
// Copyright (C) 2021 Harvey Mudd College & Oklahoma State University
//
2022-01-07 12:58:40 +00:00
// MIT LICENSE
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
2021-03-22 14:14:21 +00:00
//
2022-01-07 12:58:40 +00:00
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
2021-03-22 14:14:21 +00:00
//
2022-01-07 12:58:40 +00:00
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
// OR OTHER DEALINGS IN THE SOFTWARE.
////////////////////////////////////////////////////////////////////////////////////////////////
2021-03-22 14:14:21 +00:00
`include " wally-config.vh "
module plic (
input logic HCLK , HRESETn ,
input logic HSELPLIC ,
2021-04-16 01:09:15 +00:00
input logic [ 27 : 0 ] HADDR , // *** could factor out entryd into HADDRd at the level of uncore
2021-03-22 14:14:21 +00:00
input logic HWRITE ,
2021-04-04 10:40:53 +00:00
input logic HREADY ,
input logic [ 1 : 0 ] HTRANS ,
2021-03-22 14:14:21 +00:00
input logic [ `XLEN - 1 : 0 ] HWDATA ,
2021-04-16 01:09:15 +00:00
input logic UARTIntr , GPIOIntr ,
2021-03-22 14:14:21 +00:00
output logic [ `XLEN - 1 : 0 ] HREADPLIC ,
2021-04-04 10:40:53 +00:00
output logic HRESPPLIC , HREADYPLIC ,
output logic ExtIntM ) ;
2021-04-05 12:03:14 +00:00
localparam N = `PLIC_NUM_SRC ; // should not exceed 63; does not inlcude source 0, which does not connect to anything according to spec
2021-03-22 14:14:21 +00:00
2021-04-05 12:13:01 +00:00
logic memwrite , memread , initTrans ;
2021-05-19 18:10:48 +00:00
logic [ 23 : 0 ] entry , entryd ;
2021-04-05 03:10:33 +00:00
logic [ 31 : 0 ] Din , Dout ;
2021-04-04 10:40:53 +00:00
logic [ N: 1 ] requests ;
logic [ 2 : 0 ] intPriority [ N: 1 ] ;
2021-03-22 14:14:21 +00:00
logic [ 2 : 0 ] intThreshold ;
2021-04-04 10:40:53 +00:00
logic [ N: 1 ] intPending , nextIntPending , intEn , intInProgress ;
logic [ 5 : 0 ] intClaim ; // ID's are 6 bits if we stay within 63 sources
logic [ N: 1 ] pendingArray [ 7 : 1 ] ;
logic [ 7 : 1 ] pendingPGrouped ;
logic [ 7 : 1 ] pendingMaxP ;
logic [ N: 1 ] pendingRequestsAtMaxP ;
logic [ 7 : 1 ] threshMask ;
2021-03-22 14:14:21 +00:00
2021-04-22 15:22:01 +00:00
// =======
2021-03-22 14:14:21 +00:00
// AHB I/O
2021-04-22 15:22:01 +00:00
// =======
2021-05-19 18:10:48 +00:00
assign entry = { HADDR [ 23 : 2 ] , 2 'b0 } ;
2021-04-04 10:40:53 +00:00
assign initTrans = HREADY & HSELPLIC & ( HTRANS ! = 2 'b00 ) ;
2021-04-05 12:03:14 +00:00
assign memread = initTrans & ~ HWRITE ;
// entryd and memwrite are delayed by a cycle because AHB controller waits a cycle before outputting write data
2021-04-05 03:10:33 +00:00
flopr # ( 1 ) memwriteflop ( HCLK , ~ HRESETn , initTrans & HWRITE , memwrite ) ;
2021-05-19 18:10:48 +00:00
flopr # ( 24 ) entrydflop ( HCLK , ~ HRESETn , entry , entryd ) ;
2021-03-22 14:14:21 +00:00
assign HRESPPLIC = 0 ; // OK
2021-04-05 03:10:33 +00:00
assign HREADYPLIC = 1 'b1 ; // PLIC never takes >1 cycle to respond
2021-04-04 10:40:53 +00:00
2021-04-05 03:10:33 +00:00
// account for subword read/write circuitry
2021-04-05 12:03:14 +00:00
// -- Note PLIC registers are 32 bits no matter what; access them with LW SW.
2022-01-05 16:01:03 +00:00
if ( `XLEN = = 64 ) begin
assign Din = entryd [ 2 ] ? HWDATA [ 63 : 32 ] : HWDATA [ 31 : 0 ] ;
assign HREADPLIC = entryd [ 2 ] ? { Dout , 32 'b0 } : { 32 'b0 , Dout } ;
end else begin // 32-bit
assign Din = HWDATA [ 31 : 0 ] ;
assign HREADPLIC = Dout ;
end
2021-04-04 10:40:53 +00:00
2021-04-22 15:22:01 +00:00
// ==================
// Register Interface
// ==================
always @ ( posedge HCLK , negedge HRESETn ) begin
// resetting
if ( ~ HRESETn ) begin
2021-04-24 13:32:09 +00:00
intPriority < = # 1 ' { default : 3 'b0 } ;
2021-04-22 15:22:01 +00:00
intEn < = # 1 { N { 1 'b0 } } ;
intThreshold < = # 1 3 'b0 ;
intInProgress < = # 1 { N { 1 'b0 } } ;
// writing
2021-04-30 10:26:31 +00:00
end else begin
if ( memwrite )
casez ( entryd )
2021-05-19 18:10:48 +00:00
24 'h0000 ? ? : intPriority [ entryd [ 7 : 2 ] ] < = # 1 Din [ 2 : 0 ] ;
2021-04-30 10:26:31 +00:00
`ifdef PLIC_NUM_SRC_LT_32
2021-05-19 18:10:48 +00:00
24 'h002000 : intEn [ N: 1 ] < = # 1 Din [ N: 1 ] ;
2021-04-30 10:26:31 +00:00
`endif
`ifndef PLIC_NUM_SRC_LT_32
2021-05-19 18:10:48 +00:00
24 'h002000 : intEn [ 31 : 1 ] < = # 1 Din [ 31 : 1 ] ;
24 'h002004 : intEn [ N: 32 ] < = # 1 Din [ 31 : 0 ] ;
2021-04-30 10:26:31 +00:00
`endif
2021-05-19 18:10:48 +00:00
24 'h200000 : intThreshold [ 2 : 0 ] < = # 1 Din [ 2 : 0 ] ;
2021-06-10 14:19:10 +00:00
24 'h200004 : intInProgress < = # 1 intInProgress & ~ ( 4 'b1 < < ( Din [ 5 : 0 ] - 1 ) ) ; // lower "InProgress" to signify completion
2021-04-30 10:26:31 +00:00
endcase
// reading
if ( memread )
casez ( entry )
2021-06-10 14:19:10 +00:00
24 'h0000 ? ? : Dout < = # 1 { 29 'b0 , intPriority [ entry [ 7 : 2 ] ] } ;
2021-04-30 10:26:31 +00:00
`ifdef PLIC_NUM_SRC_LT_32
2021-05-19 18:10:48 +00:00
24 'h001000 : Dout < = # 1 { { ( 31 - N ) { 1 'b0 } } , intPending [ N: 1 ] , 1 'b0 } ;
24 'h002000 : Dout < = # 1 { { ( 31 - N ) { 1 'b0 } } , intEn [ N: 1 ] , 1 'b0 } ;
2021-04-30 10:26:31 +00:00
`endif
`ifndef PLIC_NUM_SRC_LT_32
2021-05-19 18:10:48 +00:00
24 'h001000 : Dout < = # 1 { intPending [ 31 : 1 ] , 1 'b0 } ;
24 'h001004 : Dout < = # 1 { { ( 63 - N ) { 1 'b0 } } , intPending [ N: 32 ] } ;
24 'h002000 : Dout < = # 1 { intEn [ 31 : 1 ] , 1 'b0 } ;
24 'h002004 : Dout < = # 1 { { ( 63 - N ) { 1 'b0 } } , intEn [ N: 32 ] } ;
2021-04-30 10:26:31 +00:00
`endif
2021-05-19 18:10:48 +00:00
24 'h200000 : Dout < = # 1 { 29 'b0 , intThreshold [ 2 : 0 ] } ;
24 'h200004 : begin
2021-04-30 10:26:31 +00:00
Dout < = # 1 { 26 'b0 , intClaim } ;
2021-06-10 14:19:10 +00:00
intInProgress < = # 1 intInProgress | ( 4 'b1 < < ( intClaim - 1 ) ) ; // claimed requests are currently in progress of being serviced until they are completed
2021-04-30 10:26:31 +00:00
end
2021-08-11 21:56:22 +00:00
default : Dout < = # 1 32 'h0 ; // invalid access
2021-04-30 10:26:31 +00:00
endcase
else
Dout < = # 1 32 'h0 ;
end
2021-04-22 15:22:01 +00:00
end
2021-03-22 14:14:21 +00:00
2021-04-04 10:40:53 +00:00
// connect sources to requests
2021-04-16 01:09:15 +00:00
always_comb begin
requests = { N { 1 'b0 } } ;
`ifdef PLIC_GPIO_ID
requests [ `PLIC_GPIO_ID ] = GPIOIntr ;
`endif
`ifdef PLIC_UART_ID
requests [ `PLIC_UART_ID ] = UARTIntr ;
`endif
end
2021-04-05 12:13:01 +00:00
2021-04-04 10:40:53 +00:00
// pending updates
// *** verify that this matches the expectations of the things that make requests (in terms of timing, edge-triggered vs level-triggered)
2021-06-10 14:19:10 +00:00
assign nextIntPending = ( intPending | ( requests & ~ intInProgress ) ) & // requests should raise intPending except when their service routine is already in progress
~ ( { 4 { ( ( entry = = 24 'h200004 ) & memread ) } } < < ( intClaim - 1 ) ) ; // clear pending bit when claim register is read
2021-04-04 10:40:53 +00:00
flopr # ( N ) intPendingFlop ( HCLK , ~ HRESETn , nextIntPending , intPending ) ;
2021-04-05 12:03:14 +00:00
2021-04-04 10:40:53 +00:00
// pending array - indexed by priority_lvl x source_ID
2021-07-04 23:02:56 +00:00
genvar i , j ;
2022-01-05 16:01:03 +00:00
for ( j = 1 ; j < = 7 ; j + + ) begin : pending
for ( i = 1 ; i < = N ; i = i + 1 ) begin : pendingbit
assign pendingArray [ j ] [ i ] = ( intPriority [ i ] = = j ) & intEn [ i ] & intPending [ i ] ;
2021-03-22 14:14:21 +00:00
end
2022-01-05 16:01:03 +00:00
end
2021-04-04 10:40:53 +00:00
// pending array, except grouped by priority
2021-07-04 23:17:15 +00:00
assign pendingPGrouped [ 7 : 1 ] = { | pendingArray [ 7 ] ,
2021-04-04 10:40:53 +00:00
| pendingArray [ 6 ] ,
| pendingArray [ 5 ] ,
| pendingArray [ 4 ] ,
| pendingArray [ 3 ] ,
| pendingArray [ 2 ] ,
2021-07-04 23:17:15 +00:00
| pendingArray [ 1 ] } ;
//assign pendingPGrouped = pendingArray.or;
2021-07-04 23:02:56 +00:00
2021-04-04 10:40:53 +00:00
// pendingPGrouped, except only topmost priority is active
assign pendingMaxP [ 7 : 1 ] = { pendingPGrouped [ 7 ] ,
pendingPGrouped [ 6 ] & ~ | pendingPGrouped [ 7 ] ,
pendingPGrouped [ 5 ] & ~ | pendingPGrouped [ 7 : 6 ] ,
pendingPGrouped [ 4 ] & ~ | pendingPGrouped [ 7 : 5 ] ,
pendingPGrouped [ 3 ] & ~ | pendingPGrouped [ 7 : 4 ] ,
pendingPGrouped [ 2 ] & ~ | pendingPGrouped [ 7 : 3 ] ,
pendingPGrouped [ 1 ] & ~ | pendingPGrouped [ 7 : 2 ] } ;
// select the pending requests at that priority
assign pendingRequestsAtMaxP [ N: 1 ] = ( { N { pendingMaxP [ 7 ] } } & pendingArray [ 7 ] )
2021-04-05 12:03:14 +00:00
| ( { N { pendingMaxP [ 6 ] } } & pendingArray [ 6 ] )
| ( { N { pendingMaxP [ 5 ] } } & pendingArray [ 5 ] )
| ( { N { pendingMaxP [ 4 ] } } & pendingArray [ 4 ] )
| ( { N { pendingMaxP [ 3 ] } } & pendingArray [ 3 ] )
| ( { N { pendingMaxP [ 2 ] } } & pendingArray [ 2 ] )
| ( { N { pendingMaxP [ 1 ] } } & pendingArray [ 1 ] ) ;
2021-04-04 10:40:53 +00:00
// find the lowest ID amongst active interrupts at the highest priority
2022-01-05 22:10:26 +00:00
logic [ 5 : 0 ] k ;
2021-07-04 23:17:15 +00:00
always_comb begin
intClaim = 6 'b0 ;
2022-01-05 22:10:26 +00:00
for ( k = N ; k > 0 ; k = k - 1 ) begin
if ( pendingRequestsAtMaxP [ k ] ) intClaim = k ;
2021-04-04 10:40:53 +00:00
end
2021-07-04 23:17:15 +00:00
end
2021-04-04 10:40:53 +00:00
// create threshold mask
2021-07-04 23:17:15 +00:00
always_comb begin
threshMask [ 7 ] = ( intThreshold ! = 7 ) ;
threshMask [ 6 ] = ( intThreshold ! = 6 ) & threshMask [ 7 ] ;
threshMask [ 5 ] = ( intThreshold ! = 5 ) & threshMask [ 6 ] ;
threshMask [ 4 ] = ( intThreshold ! = 4 ) & threshMask [ 5 ] ;
threshMask [ 3 ] = ( intThreshold ! = 3 ) & threshMask [ 4 ] ;
threshMask [ 2 ] = ( intThreshold ! = 2 ) & threshMask [ 3 ] ;
threshMask [ 1 ] = ( intThreshold ! = 1 ) & threshMask [ 2 ] ;
2021-05-19 18:10:48 +00:00
end
2021-04-04 10:40:53 +00:00
// is the max priority > threshold?
2021-04-05 12:03:14 +00:00
// *** would it be any better to first priority encode maxPriority into binary and then ">" with threshold?
2021-04-04 10:40:53 +00:00
assign ExtIntM = | ( threshMask & pendingPGrouped ) ;
2021-03-22 14:14:21 +00:00
endmodule