cvw/src/uncore/plic_apb.sv

246 lines
11 KiB
Systemverilog
Raw Normal View History

2022-07-06 13:26:14 +00:00
///////////////////////////////////////////
// plic_apb.sv
//
// Written: bbracker@hmc.edu 18 January 2021
// Modified:
//
// Purpose: Platform-Level Interrupt Controller
// 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.
// This PLIC implementation serves as both the PLIC Gateways and PLIC Core.
// It assumes interrupt sources are level-triggered wires.
2023-01-14 14:15:35 +00:00
//
// Documentation: RISC-V System on Chip Design Chapter 15
2022-07-06 13:26:14 +00:00
//
2023-01-11 23:15:08 +00:00
// A component of the CORE-V-WALLY configurable RISC-V project.
2022-07-06 13:26:14 +00:00
//
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
2022-07-06 13:26:14 +00:00
//
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
2022-07-06 13:26:14 +00:00
//
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
// may obtain a copy of the License at
2022-07-06 13:26:14 +00:00
//
// https://solderpad.org/licenses/SHL-2.1/
//
// Unless required by applicable law or agreed to in writing, any work distributed under the
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
2022-07-06 13:26:14 +00:00
////////////////////////////////////////////////////////////////////////////////////////////////
// number of interrupt sources
// does not include source 0, which does not connect to anything according to spec
2023-01-15 01:21:07 +00:00
// up to 63 sources supported; in the future, allow up to 1023 sources
2022-07-06 13:26:14 +00:00
`define C 2
// number of conexts
2023-01-15 01:21:07 +00:00
// hardcoded to 2 contexts for now; later upgrade to arbitrary (up to 15872) contexts
2022-07-06 13:26:14 +00:00
2023-05-26 21:24:12 +00:00
module plic_apb import cvw::*; #(parameter cvw_t P) (
2023-06-15 17:08:16 +00:00
input logic PCLK, PRESETn,
input logic PSEL,
input logic [27:0] PADDR,
2023-05-26 21:24:12 +00:00
input logic [P.XLEN-1:0] PWDATA,
input logic [P.XLEN/8-1:0] PSTRB,
2023-06-15 17:08:16 +00:00
input logic PWRITE,
input logic PENABLE,
2023-05-26 21:24:12 +00:00
output logic [P.XLEN-1:0] PRDATA,
2023-06-15 17:08:16 +00:00
output logic PREADY,
input logic UARTIntr,GPIOIntr,SDCIntr,
2023-06-15 17:08:16 +00:00
output logic MExtInt, SExtInt
2023-01-15 01:21:07 +00:00
);
2022-07-06 13:26:14 +00:00
2023-06-15 17:08:16 +00:00
logic memwrite, memread;
logic [23:0] entry;
logic [31:0] Din, Dout;
2022-07-06 13:26:14 +00:00
// context-independent signals
2023-07-05 22:30:55 +00:00
logic [P.PLIC_NUM_SRC:1] requests;
logic [P.PLIC_NUM_SRC:1][2:0] intPriority;
logic [P.PLIC_NUM_SRC:1] intInProgress, intPending, nextIntPending;
2022-07-06 13:26:14 +00:00
// context-dependent signals
2023-03-24 22:32:25 +00:00
logic [`C-1:0][2:0] intThreshold;
2023-07-05 22:30:55 +00:00
logic [`C-1:0][P.PLIC_NUM_SRC:1] intEn;
2023-03-24 22:32:25 +00:00
logic [`C-1:0][5:0] intClaim; // ID's are 6 bits if we stay within 63 sources
2023-07-05 22:30:55 +00:00
logic [`C-1:0][7:1][P.PLIC_NUM_SRC:1] irqMatrix;
2023-03-24 22:32:25 +00:00
logic [`C-1:0][7:1] priorities_with_irqs;
logic [`C-1:0][7:1] max_priority_with_irqs;
2023-07-05 22:30:55 +00:00
logic [`C-1:0][P.PLIC_NUM_SRC:1] irqs_at_max_priority;
2023-03-24 22:32:25 +00:00
logic [`C-1:0][7:1] threshMask;
2023-07-05 22:30:55 +00:00
logic [P.PLIC_NUM_SRC-1:0] One;
2022-07-06 13:26:14 +00:00
// hacks to handle gracefully PLIC_NUM_SRC being smaller than 32
// Otherwise Questa and other simulators produce part-select out of bounds even
// though sources >=32 are never used
localparam PLIC_SRC_TOP = (P.PLIC_NUM_SRC >= 32) ? P.PLIC_NUM_SRC : 1;
localparam PLIC_SRC_BOT = (P.PLIC_NUM_SRC >= 32) ? 32 : 1;
localparam PLIC_SRC_DINTOP = (P.PLIC_NUM_SRC >= 32) ? P.PLIC_NUM_SRC -32 : 0;
localparam PLIC_SRC_EXT = (P.PLIC_NUM_SRC >= 32) ? 63-P.PLIC_NUM_SRC : 31;
2022-07-06 13:26:14 +00:00
// =======
// AHB I/O
// =======
assign memwrite = PWRITE & PENABLE & PSEL; // only write in access phase
2023-06-15 17:08:16 +00:00
assign memread = ~PWRITE & PSEL; // read at start of access phase. PENABLE hasn't set up before this
assign PREADY = 1'b1; // PLIC never takes >1 cycle to respond
assign entry = {PADDR[23:2],2'b0};
2023-07-05 22:30:55 +00:00
assign One[P.PLIC_NUM_SRC-1:1] = '0; assign One[0] = 1'b1; // Vivado does not like this as a single assignment.
2022-07-06 13:26:14 +00:00
// account for subword read/write circuitry
// -- Note PLIC registers are 32 bits no matter what; access them with LW SW.
2023-05-26 21:24:12 +00:00
if (P.XLEN == 64) begin
2022-07-06 13:26:14 +00:00
assign Din = entry[2] ? PWDATA[63:32] : PWDATA[31:0];
assign PRDATA = entry[2] ? {Dout,32'b0} : {32'b0,Dout};
end else begin // 32-bit
assign PRDATA = Dout;
assign Din = PWDATA[31:0];
end
// ==================
// Register Interface
// ==================
localparam PLIC_NUM_SRC_MIN_32 = P.PLIC_NUM_SRC < 32 ? P.PLIC_NUM_SRC : 31;
2023-06-15 17:08:16 +00:00
always @(posedge PCLK) begin
2022-07-06 13:26:14 +00:00
// resetting
if (~PRESETn) begin
2023-07-05 22:30:55 +00:00
intPriority <= #1 '0;
intEn <= #1 '0;
intThreshold <= #1 '0;
intInProgress <= #1 '0;
2022-07-06 13:26:14 +00:00
// writing
end else begin
if (memwrite)
2022-07-06 13:26:14 +00:00
casez(entry)
24'h0000??: intPriority[entry[7:2]] <= #1 Din[2:0];
24'h002000: intEn[0][PLIC_NUM_SRC_MIN_32:1] <= #1 Din[PLIC_NUM_SRC_MIN_32:1];
24'h002080: intEn[1][PLIC_NUM_SRC_MIN_32:1] <= #1 Din[PLIC_NUM_SRC_MIN_32:1];
24'h002004: if (P.PLIC_NUM_SRC >= 32) intEn[0][PLIC_SRC_TOP:PLIC_SRC_BOT] <= #1 Din[PLIC_SRC_DINTOP:0];
24'h002084: if (P.PLIC_NUM_SRC >= 32) intEn[1][PLIC_SRC_TOP:PLIC_SRC_BOT] <= #1 Din[PLIC_SRC_DINTOP:0];
2023-06-15 17:08:16 +00:00
24'h200000: intThreshold[0] <= #1 Din[2:0];
2023-07-05 22:30:55 +00:00
24'h200004: intInProgress <= #1 intInProgress & ~(One << (Din[5:0]-1)); // lower "InProgress" to signify completion
2023-06-15 17:08:16 +00:00
24'h201000: intThreshold[1] <= #1 Din[2:0];
2023-07-05 22:30:55 +00:00
24'h201004: intInProgress <= #1 intInProgress & ~(One << (Din[5:0]-1)); // lower "InProgress" to signify completion
2022-07-06 13:26:14 +00:00
endcase
// Read synchronously because a read can have side effect of changing intInProgress
2023-05-26 21:24:12 +00:00
if (memread) begin
2022-07-06 13:26:14 +00:00
casez(entry)
24'h000000: Dout <= #1 32'b0; // there is no intPriority[0]
2023-03-24 22:32:25 +00:00
24'h0000??: Dout <= #1 {29'b0,intPriority[entry[7:2]]};
24'h001000: Dout <= #1 {{(31-PLIC_NUM_SRC_MIN_32){1'b0}},intPending[PLIC_NUM_SRC_MIN_32:1],1'b0};
24'h002000: Dout <= #1 {{(31-PLIC_NUM_SRC_MIN_32){1'b0}},intEn[0][PLIC_NUM_SRC_MIN_32:1],1'b0};
24'h001004: if (P.PLIC_NUM_SRC >= 32) Dout <= #1 {{(PLIC_SRC_EXT){1'b0}},intPending[PLIC_SRC_TOP:PLIC_SRC_BOT]};
24'h002004: if (P.PLIC_NUM_SRC >= 32) Dout <= #1 {{(PLIC_SRC_EXT){1'b0}},intEn[0][PLIC_SRC_TOP:PLIC_SRC_BOT]};
24'h002080: Dout <= #1 {{(31-PLIC_NUM_SRC_MIN_32){1'b0}},intEn[1][PLIC_NUM_SRC_MIN_32:1],1'b0};
24'h002084: if (P.PLIC_NUM_SRC >= 32) Dout <= #1 {{(PLIC_SRC_EXT){1'b0}},intEn[1][PLIC_SRC_TOP:PLIC_SRC_BOT]};
2022-07-06 13:26:14 +00:00
24'h200000: Dout <= #1 {29'b0,intThreshold[0]};
24'h200004: begin
Dout <= #1 {26'b0,intClaim[0]};
2023-07-05 22:30:55 +00:00
intInProgress <= #1 intInProgress | (One << (intClaim[0]-1)); // claimed requests are currently in progress of being serviced until they are completed
2022-07-06 13:26:14 +00:00
end
24'h201000: Dout <= #1 {29'b0,intThreshold[1]};
24'h201004: begin
Dout <= #1 {26'b0,intClaim[1]};
2023-07-05 22:30:55 +00:00
intInProgress <= #1 intInProgress | (One << (intClaim[1]-1)); // claimed requests are currently in progress of being serviced until they are completed
2022-07-06 13:26:14 +00:00
end
default: Dout <= #1 32'h0; // invalid access
endcase
2023-05-26 21:24:12 +00:00
end else Dout <= #1 32'h0;
2022-07-06 13:26:14 +00:00
end
end
// connect sources to requests
always_comb begin
2023-07-05 22:30:55 +00:00
requests = {P.PLIC_NUM_SRC{1'b0}};
2023-05-26 21:24:12 +00:00
if(P.PLIC_GPIO_ID != 0) requests[P.PLIC_GPIO_ID] = GPIOIntr;
if(P.PLIC_UART_ID != 0) requests[P.PLIC_UART_ID] = UARTIntr;
if(P.PLIC_SDC_ID !=0) requests[P.PLIC_SDC_ID] = SDCIntr;
2022-07-06 13:26:14 +00:00
end
2023-05-26 21:24:12 +00:00
// pending interrupt request
assign nextIntPending = (intPending | requests) & ~intInProgress;
2023-07-05 22:30:55 +00:00
flopr #(P.PLIC_NUM_SRC) intPendingFlop(PCLK,~PRESETn,nextIntPending,intPending);
2022-07-06 13:26:14 +00:00
// context-dependent signals
genvar ctx;
for (ctx=0; ctx<`C; ctx++) begin
// request matrix
// priority level (rows) X source ID (columns)
//
// irqMatrix[ctx][pri][src] is high if source <src>
// has priority level <pri> and has an "active" interrupt request
// ("active" meaning it is enabled in context <ctx> and is pending)
genvar src, pri;
for (pri=1; pri<=7; pri++) begin
2023-07-05 22:30:55 +00:00
for (src=1; src<=P.PLIC_NUM_SRC; src++) begin
2022-07-06 13:26:14 +00:00
assign irqMatrix[ctx][pri][src] = (intPriority[src]==pri) & intPending[src] & intEn[ctx][src];
end
end
// which prority levels have one or more active requests?
assign priorities_with_irqs[ctx][7:1] = {
|irqMatrix[ctx][7],
|irqMatrix[ctx][6],
|irqMatrix[ctx][5],
|irqMatrix[ctx][4],
|irqMatrix[ctx][3],
|irqMatrix[ctx][2],
|irqMatrix[ctx][1]
};
// get the highest priority level that has active requests
assign max_priority_with_irqs[ctx][7:1] = {
priorities_with_irqs[ctx][7],
priorities_with_irqs[ctx][6] & ~|priorities_with_irqs[ctx][7],
priorities_with_irqs[ctx][5] & ~|priorities_with_irqs[ctx][7:6],
priorities_with_irqs[ctx][4] & ~|priorities_with_irqs[ctx][7:5],
priorities_with_irqs[ctx][3] & ~|priorities_with_irqs[ctx][7:4],
priorities_with_irqs[ctx][2] & ~|priorities_with_irqs[ctx][7:3],
priorities_with_irqs[ctx][1] & ~|priorities_with_irqs[ctx][7:2]
};
// of the sources at the highest priority level that has active requests,
// which sources have active requests?
2023-07-05 22:30:55 +00:00
assign irqs_at_max_priority[ctx][P.PLIC_NUM_SRC:1] =
({P.PLIC_NUM_SRC{max_priority_with_irqs[ctx][7]}} & irqMatrix[ctx][7]) |
({P.PLIC_NUM_SRC{max_priority_with_irqs[ctx][6]}} & irqMatrix[ctx][6]) |
({P.PLIC_NUM_SRC{max_priority_with_irqs[ctx][5]}} & irqMatrix[ctx][5]) |
({P.PLIC_NUM_SRC{max_priority_with_irqs[ctx][4]}} & irqMatrix[ctx][4]) |
({P.PLIC_NUM_SRC{max_priority_with_irqs[ctx][3]}} & irqMatrix[ctx][3]) |
({P.PLIC_NUM_SRC{max_priority_with_irqs[ctx][2]}} & irqMatrix[ctx][2]) |
({P.PLIC_NUM_SRC{max_priority_with_irqs[ctx][1]}} & irqMatrix[ctx][1]);
2022-07-06 13:26:14 +00:00
// of the sources at the highest priority level that has active requests,
// choose the source with the lowest source ID to be the most urgent
// and set intClaim to the source ID of the most urgent active request
integer k;
always_comb begin
intClaim[ctx] = 6'b0;
2023-07-05 22:30:55 +00:00
for (k=P.PLIC_NUM_SRC; k>0; k--) begin
2022-07-06 13:26:14 +00:00
if (irqs_at_max_priority[ctx][k]) intClaim[ctx] = k[5:0];
end
end
// create threshold mask
always_comb begin
threshMask[ctx][7] = (intThreshold[ctx] != 7);
threshMask[ctx][6] = (intThreshold[ctx] != 6) & threshMask[ctx][7];
threshMask[ctx][5] = (intThreshold[ctx] != 5) & threshMask[ctx][6];
threshMask[ctx][4] = (intThreshold[ctx] != 4) & threshMask[ctx][5];
threshMask[ctx][3] = (intThreshold[ctx] != 3) & threshMask[ctx][4];
threshMask[ctx][2] = (intThreshold[ctx] != 2) & threshMask[ctx][3];
threshMask[ctx][1] = (intThreshold[ctx] != 1) & threshMask[ctx][2];
end
end
// is the max priority > threshold?
// would it be any better to first priority encode maxPriority into binary and then ">" with threshold?
2022-07-06 13:26:14 +00:00
assign MExtInt = |(threshMask[0] & priorities_with_irqs[0]);
assign SExtInt = |(threshMask[1] & priorities_with_irqs[1]);
endmodule