diff --git a/.gitignore b/.gitignore index 8b13789..f2baf14 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ +**/work/ +.bender/ +Bender.lock +modelsim.ini +transcript +vsim.wlf +memories/ +traces/ +sim/tcl/compile.tcl diff --git a/Bender.yml b/Bender.yml index 18d4c8e..18ca59c 100644 --- a/Bender.yml +++ b/Bender.yml @@ -25,6 +25,10 @@ dependencies: sources: - src/clicint_reg_pkg.sv - src/clicint_reg_top.sv + - src/clicintv_reg_pkg.sv + - src/clicintv_reg_top.sv + - src/clicvs_reg_pkg.sv + - src/clicvs_reg_top.sv - src/mclic_reg_pkg.sv - src/mclic_reg_top.sv - src/clic_reg_adapter.sv diff --git a/src/clic.sv b/src/clic.sv index 044da34..20e7ea5 100644 --- a/src/clic.sv +++ b/src/clic.sv @@ -17,15 +17,27 @@ `include "common_cells/assertions.svh" -module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( +module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_reg_pkg::*; import clicvs_reg_pkg::*; #( parameter type reg_req_t = logic, parameter type reg_rsp_t = logic, parameter int N_SOURCE = 256, + parameter int N_PIPE = 1, parameter int INTCTLBITS = 8, parameter bit SSCLIC = 0, parameter bit USCLIC = 0, + parameter bit VSCLIC = 0, // enable vCLIC (requires SSCLIC) + + // vCLIC dependent parameters + parameter int unsigned N_VSCTXTS = 0, // Number of Virtual Contexts supported. + // This implementation assumes CLIC is mapped to an address + // range that allows up to 64 contexts (at least 512KiB) + parameter bit VSPRIO = 0, // Enable VS prioritization (requires VSCLIC) + parameter int VsprioWidth = 1, // N of VS priority bits (must be set accordingly to the `clicvs` register width) + // do not edit below, these are derived - localparam int SRC_W = $clog2(N_SOURCE) + localparam int SRC_W = $clog2(N_SOURCE), + localparam int unsigned MAX_VSCTXTS = 64, // up to 64 VS contexts + localparam int unsigned VSID_W = $clog2(MAX_VSCTXTS) )( input logic clk_i, input logic rst_ni, @@ -38,42 +50,96 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( input [N_SOURCE-1:0] intr_src_i, // Interrupt notification to core - output logic irq_valid_o, - input logic irq_ready_i, - output logic [SRC_W-1:0] irq_id_o, - output logic [7:0] irq_level_o, - output logic irq_shv_o, - output logic [1:0] irq_priv_o, - output logic irq_kill_req_o, - input logic irq_kill_ack_i + output logic irq_valid_o, + input logic irq_ready_i, + output logic [SRC_W-1:0] irq_id_o, + output logic [7:0] irq_level_o, + output logic irq_shv_o, + output logic [1:0] irq_priv_o, + output logic [VSID_W-1:0] irq_vsid_o, + output logic irq_v_o, + output logic irq_kill_req_o, + input logic irq_kill_ack_i ); if (USCLIC) $fatal(1, "usclic mode is not supported"); + if (VSCLIC) begin + if (N_VSCTXTS <= 0 || N_VSCTXTS > MAX_VSCTXTS) + $fatal(1, "vsclic extension requires N_VSCTXTS in [1, 64]"); + if (!SSCLIC) + $fatal(1, "vsclic extension requires ssclic"); + end else begin + if(VSPRIO) + $fatal(1, "vsprio extension requires vsclic"); + end + localparam logic [1:0] U_MODE = 2'b00; localparam logic [1:0] S_MODE = 2'b01; localparam logic [1:0] M_MODE = 2'b11; - localparam logic [15:0] MCLICCFG_START = 16'h0000; - localparam logic [15:0] MCLICINT_START = 16'h1000; - localparam logic [15:0] MCLICINT_END = 16'h4fff; - - localparam logic [15:0] SCLICCFG_START = 16'h8000; - localparam logic [15:0] SCLICINT_START = 16'h9000; - localparam logic [15:0] SCLICINT_END = 16'hcfff; + /////////////////////////////////////////////////// + // CLIC internal addressing // + /////////////////////////////////////////////////// + // + // The address range is divided into blocks of 32KB. + // There is one block each for S-mode and M-mode, + // and there are up to MAX_VSCTXTS extra blocks, + // one per guest VS. + // + // M_MODE : [0x000000 - 0x007fff] + // S_MODE : [0x008000 - 0x00ffff] + // VS_1 : [0x010000 - 0x017fff] + // VS_2 : [0x018000 - 0x01ffff] + // : + // VS_64 : [0x208000 - 0x20ffff] + + // Some value between 16 (VSCLIC = 0) and 22 (64 VS contexts) + localparam int unsigned ADDR_W = $clog2((N_VSCTXTS + 2) * 32 * 1024); + + // Each privilege mode address space is aligned to a 32KiB physical memory region + localparam logic [ADDR_W-1:0] MCLICCFG_START = 'h00000; + localparam logic [ADDR_W-1:0] MCLICCFG_END = 'h00800; + localparam logic [ADDR_W-1:0] MCLICINT_START = 'h01000; + localparam logic [ADDR_W-1:0] MCLICINT_END = 'h04fff; + + localparam logic [ADDR_W-1:0] SCLICCFG_START = 'h08000; + localparam logic [ADDR_W-1:0] SCLICINT_START = 'h09000; + localparam logic [ADDR_W-1:0] SCLICINT_END = 'h0cfff; + localparam logic [ADDR_W-1:0] SCLICINTV_START = 'h0d000; + localparam logic [ADDR_W-1:0] SCLICINTV_END = 'h0dfff; + + localparam logic [ADDR_W-1:0] VSCLICPRIO_START = 'h0e000; + localparam logic [ADDR_W-1:0] VSCLICPRIO_END = 'h0efff; + + // VS `i` (1 <= i <= 64) will be mapped to VSCLIC*(i) address space + `define VSCLICCFG_START(i) ('h08000 * (i + 1)) + `define VSCLICINT_START(i) ('h08000 * (i + 1) + 'h01000) + `define VSCLICINT_END(i) ('h08000 * (i + 1) + 'h04fff) mclic_reg2hw_t mclic_reg2hw; clicint_reg2hw_t [N_SOURCE-1:0] clicint_reg2hw; clicint_hw2reg_t [N_SOURCE-1:0] clicint_hw2reg; + clicintv_reg2hw_t [(N_SOURCE/4)-1:0] clicintv_reg2hw; + // clicintv_hw2reg_t [(N_SOURCE/4)-1:0] clicintv_hw2reg; // Not needed + + clicvs_reg2hw_t [(MAX_VSCTXTS/4)-1:0] clicvs_reg2hw; + // clicvs_hw2reg_t [(MAX_VSCTXTS/4)-1:0] clicvs_hw2reg; // Not needed + logic [7:0] intctl [N_SOURCE]; logic [7:0] irq_max; logic [1:0] intmode [N_SOURCE]; logic [1:0] irq_mode; + logic [VSID_W-1:0] vsid [N_SOURCE]; // Per-IRQ Virtual Supervisor (VS) ID + logic intv [N_SOURCE]; // Per-IRQ virtualization bit + + logic [VsprioWidth-1:0] vsprio [MAX_VSCTXTS]; // Per-VS priority + logic [N_SOURCE-1:0] le; // 0: level-sensitive 1: edge-sensitive logic [N_SOURCE-1:0] ip; logic [N_SOURCE-1:0] ie; @@ -81,6 +147,7 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( logic [N_SOURCE-1:0] shv; // Handle per-irq SHV bits logic [N_SOURCE-1:0] claim; + logic mnxti_cfg; // handle incoming interrupts clic_gateway #( @@ -100,9 +167,13 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( // generate interrupt depending on ip, ie, level and priority clic_target #( - .N_SOURCE (N_SOURCE), - .PrioWidth (INTCTLBITS), - .ModeWidth (2) + .N_SOURCE (N_SOURCE), + .N_PIPE (N_PIPE), + .MAX_VSCTXTS (MAX_VSCTXTS), + .PrioWidth (INTCTLBITS), + .ModeWidth (2), + .VsidWidth (VSID_W), + .VsprioWidth (VsprioWidth) ) i_clic_target ( .clk_i, .rst_ni, @@ -110,9 +181,14 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( .ip_i (ip), .ie_i (ie), .le_i (le), + .shv_i (shv), .prio_i (intctl), .mode_i (intmode), + .intv_i (intv), + .vsid_i (vsid), + + .vsprio_i (vsprio), .claim_o (claim), @@ -121,9 +197,14 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( .irq_id_o, .irq_max_o (irq_max), .irq_mode_o (irq_mode), + .irq_v_o, + .irq_vsid_o, + .irq_shv_o, .irq_kill_req_o, - .irq_kill_ack_i + .irq_kill_ack_i, + + .mnxti_cfg_i (mnxti_cfg) ); // configuration registers @@ -150,14 +231,14 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( // 0x1000 - 0x4fff (machine mode) reg_req_t reg_all_int_req; reg_rsp_t reg_all_int_rsp; - logic [15:0] int_addr; + logic [ADDR_W-1:0] int_addr; reg_req_t [N_SOURCE-1:0] reg_int_req; reg_rsp_t [N_SOURCE-1:0] reg_int_rsp; // TODO: improve decoding by only deasserting valid always_comb begin - int_addr = reg_all_int_req.addr[15:2]; + int_addr = reg_all_int_req.addr[ADDR_W-1:2]; reg_int_req = '0; reg_all_int_rsp = '0; @@ -184,21 +265,126 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( ); end - // configuration registers - // 0x8000 (supervisor mode) + // interrupt control and status registers (per interrupt line) + // 0x???? - 0x???? (machine mode) + reg_req_t reg_all_v_req; + reg_rsp_t reg_all_v_rsp; + logic [ADDR_W-1:0] v_addr; + + reg_req_t [(N_SOURCE/4)-1:0] reg_v_req; + reg_rsp_t [(N_SOURCE/4)-1:0] reg_v_rsp; + + // VSPRIO register interface signals + reg_req_t reg_all_vs_req; + reg_rsp_t reg_all_vs_rsp; + logic [ADDR_W-1:0] vs_addr; + + reg_req_t [(MAX_VSCTXTS/4)-1:0] reg_vs_req; + reg_rsp_t [(MAX_VSCTXTS/4)-1:0] reg_vs_rsp; + + if (VSCLIC) begin + + always_comb begin + reg_v_req = '0; + reg_all_v_rsp = '0; + + v_addr = reg_all_v_req.addr[ADDR_W-1:2]; + + reg_v_req[v_addr] = reg_all_v_req; + reg_all_v_rsp = reg_v_rsp[v_addr]; + end + + for (genvar i = 0; i < (N_SOURCE/4); i++) begin : gen_clic_intv + clicintv_reg_top #( + .reg_req_t (reg_req_t), + .reg_rsp_t (reg_rsp_t) + ) i_clicintv_reg_top ( + .clk_i, + .rst_ni, + + .reg_req_i (reg_v_req[i]), + .reg_rsp_o (reg_v_rsp[i]), + + .reg2hw (clicintv_reg2hw[i]), + // .hw2reg (clicintv_hw2reg[i]), + + .devmode_i (1'b1) + ); + end + + if (VSPRIO) begin + + always_comb begin + reg_vs_req = '0; + reg_all_vs_rsp = '0; + + vs_addr = reg_all_vs_req.addr[ADDR_W-1:2]; + + reg_vs_req[vs_addr] = reg_all_vs_req; + reg_all_vs_rsp = reg_vs_rsp[vs_addr]; + end + + for(genvar i = 0; i < (MAX_VSCTXTS/4); i++) begin : gen_clic_vs + + clicvs_reg_top #( + .reg_req_t (reg_req_t), + .reg_rsp_t (reg_rsp_t) + ) i_clicvs_reg_top ( + .clk_i, + .rst_ni, + + .reg_req_i (reg_vs_req[i]), + .reg_rsp_o (reg_vs_rsp[i]), + + .reg2hw (clicvs_reg2hw[i]), + // .hw2reg (clicvs_hw2reg[i]), + + .devmode_i (1'b1) + ); + + end - // interrupt control and status register - // 0x9000 - 0xcfff (supervisor mode) - // mirror + end else begin + assign clicvs_reg2hw = '0; + // assign clicvs_hw2reg = '0; + assign reg_vs_req = '0; + assign reg_vs_rsp = '0; + assign vs_addr = '0; + assign reg_all_vs_rsp = '0; + end + + end else begin + // If both V and Vprio are not enabled, tie all to 0' + // VS + assign clicintv_reg2hw = '0; + assign reg_v_req = '0; + assign reg_v_rsp = '0; + assign v_addr = '0; + assign reg_all_v_rsp = '0; + // VSprio + assign clicvs_reg2hw = '0; + assign reg_vs_req = '0; + assign reg_vs_rsp = '0; + assign vs_addr = '0; + assign reg_all_vs_rsp = '0; + end // top level address decoding and bus muxing + + // Helper signal used to store intermediate address + logic [ADDR_W-1:0] addr_tmp; + always_comb begin : clic_addr_decode - reg_mclic_req = '0; + reg_mclic_req = '0; reg_all_int_req = '0; - reg_rsp_o = '0; + reg_all_v_req = '0; + reg_all_vs_req = '0; + reg_rsp_o = '0; + + addr_tmp = '0; - unique case(reg_req_i.addr[15:0]) inside - MCLICCFG_START: begin + unique case(reg_req_i.addr[ADDR_W-1:0]) inside + [MCLICCFG_START:MCLICCFG_END]: begin reg_mclic_req = reg_req_i; reg_rsp_o = reg_mclic_rsp; end @@ -215,10 +401,11 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( end [SCLICINT_START:SCLICINT_END]: begin if (SSCLIC) begin - reg_all_int_req.addr = reg_req_i.addr - SCLICINT_START; - if (intmode[reg_all_int_req.addr[15:2]] <= S_MODE) begin + addr_tmp = reg_req_i.addr[ADDR_W-1:0] - SCLICINT_START; + if (intmode[addr_tmp[ADDR_W-1:2]] <= S_MODE) begin // check whether the irq we want to access is s-mode or lower reg_all_int_req = reg_req_i; + reg_all_int_req.addr = addr_tmp; // Prevent setting interrupt mode to m-mode . This is currently a // bit ugly but will be nicer once we do away with auto generated // clicint registers @@ -232,6 +419,48 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( end end end + [SCLICINTV_START:SCLICINTV_END]: begin + if (VSCLIC) begin + addr_tmp = reg_req_i.addr[ADDR_W-1:0] - SCLICINTV_START; + reg_all_v_req = reg_req_i; + reg_all_v_req.addr = addr_tmp; + addr_tmp = {addr_tmp[ADDR_W-1:2], 2'b0}; + reg_rsp_o = reg_all_v_rsp; + if(intmode[addr_tmp + 0] > S_MODE) begin + reg_all_v_req.wdata[7:0] = 8'b0; + reg_rsp_o.rdata[7:0] = 8'b0; + end + if(intmode[addr_tmp + 1] > S_MODE) begin + reg_all_v_req.wdata[15:8] = 8'b0; + reg_rsp_o.rdata[15:8] = 8'b0; + end + if(intmode[addr_tmp + 2] > S_MODE) begin + reg_all_v_req.wdata[23:16] = 8'b0; + reg_rsp_o.rdata[23:16] = 8'b0; + end + if(intmode[addr_tmp + 3] > S_MODE) begin + reg_all_v_req.wdata[31:24] = 8'b0; + reg_rsp_o.rdata[31:24] = 8'b0; + end + end else begin + // VSCLIC disabled + reg_rsp_o.rdata = '0; + reg_rsp_o.error = '0; + reg_rsp_o.ready = 1'b1; + end + end + [VSCLICPRIO_START:VSCLICPRIO_END]: begin + if(VSCLIC && VSPRIO) begin + addr_tmp = reg_req_i.addr[ADDR_W-1:0] - VSCLICPRIO_START; + reg_all_vs_req = reg_req_i; + reg_all_vs_req.addr = addr_tmp; + reg_rsp_o = reg_all_vs_rsp; + end else begin + reg_rsp_o.rdata = '0; + reg_rsp_o.error = '0; + reg_rsp_o.ready = 1'b1; + end + end default: begin // inaccesible (all zero) reg_rsp_o.rdata = '0; @@ -239,12 +468,47 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( reg_rsp_o.ready = 1'b1; end endcase // unique case (reg_req_i.addr) + + // Match VS address space + if (VSCLIC) begin + for (int i = 1; i <= N_VSCTXTS; i++) begin + if (reg_req_i.addr[ADDR_W-1:0] == `VSCLICCFG_START(i)) begin + // inaccesible (all zero) + reg_rsp_o.rdata = '0; + reg_rsp_o.error = '0; + reg_rsp_o.ready = 1'b1; + end else if (`VSCLICINT_START(i) <= reg_req_i.addr[ADDR_W-1:0] && + reg_req_i.addr[ADDR_W-1:0] <= `VSCLICINT_END(i)) begin + addr_tmp = reg_req_i.addr[ADDR_W-1:0] - `VSCLICINT_START(i); + if ((intmode[addr_tmp[ADDR_W-1:2]] == S_MODE) && + (intv[addr_tmp[ADDR_W-1:2]]) && + (vsid[addr_tmp[ADDR_W-1:2]] == i)) begin + // check whether the irq we want to access is s-mode and its v bit is set and the VSID corresponds + reg_all_int_req = reg_req_i; + reg_all_int_req.addr = addr_tmp; + // Prevent setting interrupt mode to m-mode . This is currently a + // bit ugly but will be nicer once we do away with auto generated + // clicint registers + reg_all_int_req.wdata[23] = 1'b0; + reg_rsp_o = reg_all_int_rsp; + end else begin + // inaccesible (all zero) + reg_rsp_o.rdata = '0; + reg_rsp_o.error = '0; + reg_rsp_o.ready = 1'b1; + end + end + end + end end // adapter clic_reg_adapter #( - .N_SOURCE (N_SOURCE), - .INTCTLBITS (INTCTLBITS) + .N_SOURCE (N_SOURCE), + .INTCTLBITS (INTCTLBITS), + .MAX_VSCTXTS (MAX_VSCTXTS), + .VsidWidth (VSID_W), + .VsprioWidth (VsprioWidth) ) i_clic_reg_adapter ( .clk_i, .rst_ni, @@ -254,14 +518,24 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( .clicint_reg2hw, .clicint_hw2reg, + .clicintv_reg2hw, + // .clicintv_hw2reg, + + .clicvs_reg2hw, + // .clicvs_hw2reg, + .intctl_o (intctl), .intmode_o (intmode), .shv_o (shv), + .vsid_o (vsid), + .intv_o (intv), + .vsprio_o (vsprio), .ip_sw_o (ip_sw), .ie_o (ie), .le_o (le), - .ip_i (ip) + .ip_i (ip), + .mnxti_cfg_o(mnxti_cfg) ); // Create level and prio signals with dynamic indexing (#bits are read from @@ -275,9 +549,6 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( mnlbits = mclic_reg2hw.mcliccfg.mnlbits.q; end - // Extract SHV bit for the highest level, highest priority pending interrupt - assign irq_shv_o = shv[irq_id_o]; - logic [7:0] irq_level_tmp; always_comb begin @@ -323,10 +594,10 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( // m-mode only supported means no configuration nmbits = 2'b0; - if (SSCLIC || USCLIC) + if (VSCLIC || SSCLIC || USCLIC) nmbits[0] = mclic_reg2hw.mcliccfg.nmbits.q[0]; - if (SSCLIC && USCLIC) + if ((VSCLIC || SSCLIC) && USCLIC) nmbits[1] = mclic_reg2hw.mcliccfg.nmbits.q[1]; end diff --git a/src/clic_reg_adapter.sv b/src/clic_reg_adapter.sv index 70008f4..6acc1a1 100644 --- a/src/clic_reg_adapter.sv +++ b/src/clic_reg_adapter.sv @@ -14,9 +14,12 @@ // SPDX-License-Identifier: Apache-2.0 -module clic_reg_adapter import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( +module clic_reg_adapter import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_reg_pkg::*; import clicvs_reg_pkg::*; #( parameter int N_SOURCE = 32, - parameter int INTCTLBITS = 8 + parameter int INTCTLBITS = 8, + parameter int unsigned MAX_VSCTXTS = 64, + parameter int unsigned VsidWidth = 6, + parameter int unsigned VsprioWidth = 8 )( input logic clk_i, input logic rst_ni, @@ -26,14 +29,24 @@ module clic_reg_adapter import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( input clicint_reg_pkg::clicint_reg2hw_t [N_SOURCE-1:0] clicint_reg2hw, output clicint_reg_pkg::clicint_hw2reg_t [N_SOURCE-1:0] clicint_hw2reg, - output logic [7:0] intctl_o [N_SOURCE], - output logic [1:0] intmode_o [N_SOURCE], + input clicintv_reg_pkg::clicintv_reg2hw_t [(N_SOURCE/4)-1:0] clicintv_reg2hw, + // output clicintv_reg_pkg::clicintv_hw2reg_t [(N_SOURCE/4)-1:0] clicintv_hw2reg, + + input clicvs_reg_pkg::clicvs_reg2hw_t [(MAX_VSCTXTS/4)-1:0] clicvs_reg2hw, + // output clicvs_reg_pkg::clicvs_hw2reg_t [(MAX_VSCTXTS/4)-1:0] clicvs_hw2reg, + + output logic [7:0] intctl_o [N_SOURCE], + output logic [1:0] intmode_o [N_SOURCE], + output logic [VsidWidth-1:0] vsid_o [N_SOURCE], // interrupt VS id + output logic intv_o [N_SOURCE], // interrupt virtualization + output logic [VsprioWidth-1:0] vsprio_o [MAX_VSCTXTS], // VS priority output logic [N_SOURCE-1:0] shv_o, output logic [N_SOURCE-1:0] ip_sw_o, output logic [N_SOURCE-1:0] ie_o, output logic [N_SOURCE-1:0] le_o, - input logic [N_SOURCE-1:0] ip_i + input logic [N_SOURCE-1:0] ip_i, + output logic mnxti_cfg_o ); // We only support positive edge triggered and positive level triggered @@ -50,4 +63,24 @@ module clic_reg_adapter import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( assign le_o[i] = clicint_reg2hw[i].clicint.attr_trig.q[0]; end + for (genvar i = 0; i < N_SOURCE; i = i + 4) begin : gen_reghw_v + assign vsid_o[i+0] = clicintv_reg2hw[i/4].clicintv.vsid0.q; + assign intv_o[i+0] = clicintv_reg2hw[i/4].clicintv.v0.q; + assign vsid_o[i+1] = clicintv_reg2hw[i/4].clicintv.vsid1.q; + assign intv_o[i+1] = clicintv_reg2hw[i/4].clicintv.v1.q; + assign vsid_o[i+2] = clicintv_reg2hw[i/4].clicintv.vsid2.q; + assign intv_o[i+2] = clicintv_reg2hw[i/4].clicintv.v2.q; + assign vsid_o[i+3] = clicintv_reg2hw[i/4].clicintv.vsid3.q; + assign intv_o[i+3] = clicintv_reg2hw[i/4].clicintv.v3.q; + end + + for (genvar i = 0; i < MAX_VSCTXTS; i = i + 4) begin : gen_reghw_vs + assign vsprio_o[i+0] = clicvs_reg2hw[i/4].vsprio.prio0.q; + assign vsprio_o[i+1] = clicvs_reg2hw[i/4].vsprio.prio1.q; + assign vsprio_o[i+2] = clicvs_reg2hw[i/4].vsprio.prio2.q; + assign vsprio_o[i+3] = clicvs_reg2hw[i/4].vsprio.prio3.q; + end + + assign mnxti_cfg_o = mclic_reg2hw.clicmnxticonf.q; + endmodule // clic_reg_adapter diff --git a/src/clic_target.sv b/src/clic_target.sv index e680049..03d7cc5 100644 --- a/src/clic_target.sv +++ b/src/clic_target.sv @@ -29,9 +29,13 @@ `include "common_cells/assertions.svh" module clic_target #( - parameter int unsigned N_SOURCE = 256, - parameter int unsigned PrioWidth = 8, - parameter int unsigned ModeWidth = 2, + parameter int unsigned N_SOURCE = 256, + parameter int unsigned N_PIPE = 1, + parameter int unsigned MAX_VSCTXTS = 64, + parameter int unsigned PrioWidth = 8, + parameter int unsigned ModeWidth = 2, + parameter int unsigned VsidWidth = 6, + parameter int unsigned VsprioWidth = 8, // derived parameters do not change this localparam int SrcWidth = $clog2(N_SOURCE) // derived parameter @@ -42,9 +46,14 @@ module clic_target #( input [N_SOURCE-1:0] ip_i, input [N_SOURCE-1:0] ie_i, input [N_SOURCE-1:0] le_i, + input [N_SOURCE-1:0] shv_i, input [PrioWidth-1:0] prio_i [N_SOURCE], input [ModeWidth-1:0] mode_i [N_SOURCE], + input logic intv_i [N_SOURCE], + input [VsidWidth-1:0] vsid_i [N_SOURCE], + + input [VsprioWidth-1:0] vsprio_i [MAX_VSCTXTS], output logic [N_SOURCE-1:0] claim_o, @@ -53,21 +62,34 @@ module clic_target #( output logic [SrcWidth-1:0] irq_id_o, output logic [PrioWidth-1:0] irq_max_o, output logic [ModeWidth-1:0] irq_mode_o, + output logic [VsidWidth-1:0] irq_vsid_o, + output logic irq_v_o, + output logic irq_shv_o, output logic irq_kill_req_o, - input logic irq_kill_ack_i + input logic irq_kill_ack_i, + input logic mnxti_cfg_i ); // this only works with 2 or more sources `ASSERT_INIT(NumSources_A, N_SOURCE >= 2) + typedef struct packed { + logic [ModeWidth-1:0] mode; + logic [VsprioWidth-1:0] vsprio; + logic [PrioWidth-1:0] prio; + } prio_t; + + localparam logic [1:0] U_MODE = 2'b00; + localparam logic [1:0] S_MODE = 2'b01; + localparam logic [1:0] M_MODE = 2'b11; + // align to powers of 2 for simplicity // a full binary tree with N levels has 2**N + 2**N-1 nodes localparam int NumLevels = $clog2(N_SOURCE); - logic [2**(NumLevels+1)-2:0] is_tree; - logic [2**(NumLevels+1)-2:0][SrcWidth-1:0] id_tree; - logic [2**(NumLevels+1)-2:0][PrioWidth-1:0] max_tree; - logic [2**(NumLevels+1)-2:0][ModeWidth-1:0] mode_tree; + logic [2**(NumLevels+1)-2:0] is_tree, is_tree_q; + logic [2**(NumLevels+1)-2:0][SrcWidth-1:0] id_tree, id_tree_q; + prio_t [2**(NumLevels+1)-2:0] max_tree, max_tree_q; for (genvar level = 0; level < NumLevels+1; level++) begin : gen_tree // @@ -95,13 +117,17 @@ module clic_target #( if (offset < N_SOURCE) begin : gen_assign assign is_tree[Pa] = ip_i[offset] & ie_i[offset]; assign id_tree[Pa] = offset; - assign max_tree[Pa] = prio_i[offset]; - assign mode_tree[Pa] = mode_i[offset]; + // NOTE: save space by encoding the Virtualization bit in the privilege mode tree fields. + // This is done by temporarily elevating the privilege level of hypervisor IRQs (mode=S_MODE, intv=0) + // to the reserved value 2'b10 so that they have higher priority than virtualized IRQs (S_MODE == 1'b01) + // but still lower priority than M_MODE IRQs (M_MODE == 2'b11). + assign max_tree[Pa].mode = ((mode_i[offset] == S_MODE) && ~intv_i[offset]) ? 2'b10 : mode_i[offset]; + assign max_tree[Pa].vsprio = ((mode_i[offset] == S_MODE) && intv_i[offset]) ? vsprio_i[vsid_i[offset]] : '0; + assign max_tree[Pa].prio = prio_i[offset]; end else begin : gen_tie_off assign is_tree[Pa] = '0; assign id_tree[Pa] = '0; assign max_tree[Pa] = '0; - assign mode_tree[Pa] = '0; end // this creates the node assignments end else begin : gen_nodes @@ -123,16 +149,18 @@ module clic_target #( // in case only one of the parent has a pending irq_o, forward that one // in case both irqs are pending, forward the one with higher priority assign sel = (~is_tree[C0] & is_tree[C1]) | - (is_tree[C0] & is_tree[C1] & ((logic'(mode_tree[C1] > mode_tree[C0]) | ((logic'(mode_tree[C1] == mode_tree[C0]) & (logic'(max_tree[C1] > max_tree[C0]))))))); + (is_tree[C0] & is_tree[C1] & logic'(max_tree[C1] > max_tree[C0])); // forwarding muxes assign is_tree[Pa] = (sel & is_tree[C1]) | ((~sel) & is_tree[C0]); assign id_tree[Pa] = ({SrcWidth{sel}} & id_tree[C1]) | ({SrcWidth{~sel}} & id_tree[C0]); - assign max_tree[Pa] = ({PrioWidth{sel}} & max_tree[C1]) | - ({PrioWidth{~sel}} & max_tree[C0]); - assign mode_tree[Pa] = ({ModeWidth{sel}} & mode_tree[C1]) | - ({ModeWidth{~sel}} & mode_tree[C0]); + assign max_tree[Pa].mode = ({ModeWidth{sel}} & max_tree[C1].mode) | + ({ModeWidth{~sel}} & max_tree[C0].mode); + assign max_tree[Pa].vsprio = ({VsprioWidth{sel}} & max_tree[C1].vsprio) | + ({VsprioWidth{~sel}} & max_tree[C0].vsprio); + assign max_tree[Pa].prio = ({PrioWidth{sel}} & max_tree[C1].prio) | + ({PrioWidth{~sel}} & max_tree[C0].prio); end end : gen_level end : gen_tree @@ -142,13 +170,30 @@ module clic_target #( logic irq_kill_req_d, irq_kill_req_q; logic higher_irq; logic [SrcWidth-1:0] irq_root_id, irq_id_d, irq_id_q; - logic [PrioWidth-1:0] irq_max_d, irq_max_q; - logic [ModeWidth-1:0] irq_mode_d, irq_mode_q; + prio_t irq_max_d, irq_max_q; + logic [VsidWidth-1:0] vsid_max_d, vsid_max_q; + logic shv_max_d, shv_max_q; + + if (N_PIPE == 0) begin : gen_no_pipe + assign max_tree_q = max_tree; + assign is_tree_q = is_tree; + assign id_tree_q = id_tree; + end else begin: gen_pipe + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + max_tree_q <= '0; + is_tree_q <= '0; + id_tree_q <= '0; + end else begin + max_tree_q <= max_tree; + is_tree_q <= is_tree; + id_tree_q <= id_tree; + end + end + end - // the results can be found at the tree root - // TODO: remove useless inequality comparison - assign irq_root_valid = (max_tree[0] > '0) ? is_tree[0] : 1'b0; - assign irq_root_id = (is_tree[0]) ? id_tree[0] : '0; + assign irq_root_valid = (max_tree_q[0] > '0) ? is_tree_q[0] : 1'b0; + assign irq_root_id = (is_tree_q[0]) ? id_tree_q[0] : '0; // higher level interrupt is available than the one we are currently processing // TODO: maybe add pipe? @@ -164,7 +209,8 @@ module clic_target #( always_comb begin irq_id_d = '0; // default: No Interrupt irq_max_d = '0; - irq_mode_d = '0; + vsid_max_d = '0; + shv_max_d = '0; claim_o = '0; irq_valid_d = 1'b0; @@ -177,17 +223,31 @@ module clic_target #( IDLE: if (irq_root_valid) begin irq_id_d = irq_root_id; - irq_max_d = max_tree[0]; - irq_mode_d = mode_tree[0]; + irq_max_d = max_tree_q[0]; + vsid_max_d = vsid_i[irq_root_id]; + shv_max_d = shv_i[irq_root_id]; irq_valid_d = 1'b1; irq_state_d = ACK; end // wait for handshake ACK: begin irq_valid_d = 1'b1; - irq_id_d = irq_id_q; - irq_max_d = irq_max_q; - irq_mode_d = irq_mode_q; + if (!mnxti_cfg_i) begin + irq_id_d = irq_id_q; + irq_max_d = irq_max_q; + vsid_max_d = vsid_max_q; + shv_max_d = shv_max_q; + end else begin + if (irq_root_valid) begin + irq_id_d = irq_root_id; // give irq_id_d the most updated value + irq_max_d = max_tree_q[0]; // give irq_max_d the most updated value + shv_max_d = shv_i[irq_root_id]; + end else begin + irq_id_d = '0; + irq_max_d = '0; + shv_max_d = shv_max_q; + end + end // level sensitive interrupts (le_i == 1'b0) can be cleared (ip_i goes // to 1'b0) and shouldn't fire anymore so we should get unstuck here if (!le_i[irq_id_q] && !ip_i[irq_id_q]) begin @@ -225,14 +285,16 @@ module clic_target #( irq_valid_q <= 1'b0; irq_id_q <= '0; irq_max_q <= '0; - irq_mode_q <= '0; + vsid_max_q <= '0; + shv_max_q <= '0; irq_kill_req_q <= 1'b0; irq_state_q <= IDLE; end else begin irq_valid_q <= irq_valid_d; irq_id_q <= irq_id_d; irq_max_q <= irq_max_d; - irq_mode_q <= irq_mode_d; + vsid_max_q <= vsid_max_d; + shv_max_q <= shv_max_d; irq_kill_req_q <= irq_kill_req_d; irq_state_q <= irq_state_d; end @@ -241,8 +303,12 @@ module clic_target #( assign irq_valid_o = irq_valid_q; assign irq_id_o = irq_id_q; - assign irq_max_o = irq_max_q; - assign irq_mode_o = irq_mode_q; + assign irq_max_o = irq_max_q.prio; + // NOTE: If the interrupt priority was modified (see note above), restore nominal privilege + assign irq_mode_o = (irq_max_q.mode == 2'b10) ? S_MODE : irq_max_q.mode; + assign irq_vsid_o = vsid_max_q; + assign irq_v_o = logic'(irq_max_q.mode == S_MODE); + assign irq_shv_o = shv_max_q; assign irq_kill_req_o = irq_kill_req_q; diff --git a/src/clicintv_reg_pkg.sv b/src/clicintv_reg_pkg.sv new file mode 100644 index 0000000..e59878e --- /dev/null +++ b/src/clicintv_reg_pkg.sv @@ -0,0 +1,62 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package clicintv_reg_pkg; + + // Address widths within the block + parameter int BlockAw = 2; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + struct packed { + logic q; + } v0; + struct packed { + logic [5:0] q; + } vsid0; + struct packed { + logic q; + } v1; + struct packed { + logic [5:0] q; + } vsid1; + struct packed { + logic q; + } v2; + struct packed { + logic [5:0] q; + } vsid2; + struct packed { + logic q; + } v3; + struct packed { + logic [5:0] q; + } vsid3; + } clicintv_reg2hw_clicintv_reg_t; + + // Register -> HW type + typedef struct packed { + clicintv_reg2hw_clicintv_reg_t clicintv; // [27:0] + } clicintv_reg2hw_t; + + // Register offsets + parameter logic [BlockAw-1:0] CLICINTV_CLICINTV_OFFSET = 2'h 0; + + // Register index + typedef enum int { + CLICINTV_CLICINTV + } clicintv_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] CLICINTV_PERMIT [1] = '{ + 4'b 1111 // index[0] CLICINTV_CLICINTV + }; + +endpackage + diff --git a/src/clicintv_reg_top.sv b/src/clicintv_reg_top.sv new file mode 100644 index 0000000..3d2e599 --- /dev/null +++ b/src/clicintv_reg_top.sv @@ -0,0 +1,429 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + +`include "common_cells/assertions.svh" + +module clicintv_reg_top #( + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter int AW = 2 +) ( + input logic clk_i, + input logic rst_ni, + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + // To HW + output clicintv_reg_pkg::clicintv_reg2hw_t reg2hw, // Write + + + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + + import clicintv_reg_pkg::* ; + + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [BlockAw-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + // Below register interface can be changed + reg_req_t reg_intf_req; + reg_rsp_t reg_intf_rsp; + + + assign reg_intf_req = reg_req_i; + assign reg_rsp_o = reg_intf_rsp; + + + assign reg_we = reg_intf_req.valid & reg_intf_req.write; + assign reg_re = reg_intf_req.valid & ~reg_intf_req.write; + assign reg_addr = reg_intf_req.addr[BlockAw-1:0]; + assign reg_wdata = reg_intf_req.wdata; + assign reg_be = reg_intf_req.wstrb; + assign reg_intf_rsp.rdata = reg_rdata; + assign reg_intf_rsp.error = reg_error; + assign reg_intf_rsp.ready = 1'b1; + + assign reg_rdata = reg_rdata_next ; + assign reg_error = (devmode_i & addrmiss) | wr_err; + + + // Define SW related signals + // Format: __{wd|we|qs} + // or _{wd|we|qs} if field == 1 or 0 + logic clicintv_v0_qs; + logic clicintv_v0_wd; + logic clicintv_v0_we; + logic [5:0] clicintv_vsid0_qs; + logic [5:0] clicintv_vsid0_wd; + logic clicintv_vsid0_we; + logic clicintv_v1_qs; + logic clicintv_v1_wd; + logic clicintv_v1_we; + logic [5:0] clicintv_vsid1_qs; + logic [5:0] clicintv_vsid1_wd; + logic clicintv_vsid1_we; + logic clicintv_v2_qs; + logic clicintv_v2_wd; + logic clicintv_v2_we; + logic [5:0] clicintv_vsid2_qs; + logic [5:0] clicintv_vsid2_wd; + logic clicintv_vsid2_we; + logic clicintv_v3_qs; + logic clicintv_v3_wd; + logic clicintv_v3_we; + logic [5:0] clicintv_vsid3_qs; + logic [5:0] clicintv_vsid3_wd; + logic clicintv_vsid3_we; + + // Register instances + // R[clicintv]: V(False) + + // F[v0]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_clicintv_v0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicintv_v0_we), + .wd (clicintv_v0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicintv.v0.q ), + + // to register interface (read) + .qs (clicintv_v0_qs) + ); + + + // F[vsid0]: 7:2 + prim_subreg #( + .DW (6), + .SWACCESS("RW"), + .RESVAL (6'h0) + ) u_clicintv_vsid0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicintv_vsid0_we), + .wd (clicintv_vsid0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicintv.vsid0.q ), + + // to register interface (read) + .qs (clicintv_vsid0_qs) + ); + + + // F[v1]: 8:8 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_clicintv_v1 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicintv_v1_we), + .wd (clicintv_v1_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicintv.v1.q ), + + // to register interface (read) + .qs (clicintv_v1_qs) + ); + + + // F[vsid1]: 15:10 + prim_subreg #( + .DW (6), + .SWACCESS("RW"), + .RESVAL (6'h0) + ) u_clicintv_vsid1 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicintv_vsid1_we), + .wd (clicintv_vsid1_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicintv.vsid1.q ), + + // to register interface (read) + .qs (clicintv_vsid1_qs) + ); + + + // F[v2]: 16:16 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_clicintv_v2 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicintv_v2_we), + .wd (clicintv_v2_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicintv.v2.q ), + + // to register interface (read) + .qs (clicintv_v2_qs) + ); + + + // F[vsid2]: 23:18 + prim_subreg #( + .DW (6), + .SWACCESS("RW"), + .RESVAL (6'h0) + ) u_clicintv_vsid2 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicintv_vsid2_we), + .wd (clicintv_vsid2_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicintv.vsid2.q ), + + // to register interface (read) + .qs (clicintv_vsid2_qs) + ); + + + // F[v3]: 24:24 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_clicintv_v3 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicintv_v3_we), + .wd (clicintv_v3_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicintv.v3.q ), + + // to register interface (read) + .qs (clicintv_v3_qs) + ); + + + // F[vsid3]: 31:26 + prim_subreg #( + .DW (6), + .SWACCESS("RW"), + .RESVAL (6'h0) + ) u_clicintv_vsid3 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicintv_vsid3_we), + .wd (clicintv_vsid3_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicintv.vsid3.q ), + + // to register interface (read) + .qs (clicintv_vsid3_qs) + ); + + + + + logic [0:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == CLICINTV_CLICINTV_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[0] & (|(CLICINTV_PERMIT[0] & ~reg_be))))); + end + + assign clicintv_v0_we = addr_hit[0] & reg_we & !reg_error; + assign clicintv_v0_wd = reg_wdata[0]; + + assign clicintv_vsid0_we = addr_hit[0] & reg_we & !reg_error; + assign clicintv_vsid0_wd = reg_wdata[7:2]; + + assign clicintv_v1_we = addr_hit[0] & reg_we & !reg_error; + assign clicintv_v1_wd = reg_wdata[8]; + + assign clicintv_vsid1_we = addr_hit[0] & reg_we & !reg_error; + assign clicintv_vsid1_wd = reg_wdata[15:10]; + + assign clicintv_v2_we = addr_hit[0] & reg_we & !reg_error; + assign clicintv_v2_wd = reg_wdata[16]; + + assign clicintv_vsid2_we = addr_hit[0] & reg_we & !reg_error; + assign clicintv_vsid2_wd = reg_wdata[23:18]; + + assign clicintv_v3_we = addr_hit[0] & reg_we & !reg_error; + assign clicintv_v3_wd = reg_wdata[24]; + + assign clicintv_vsid3_we = addr_hit[0] & reg_we & !reg_error; + assign clicintv_vsid3_wd = reg_wdata[31:26]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = clicintv_v0_qs; + reg_rdata_next[7:2] = clicintv_vsid0_qs; + reg_rdata_next[8] = clicintv_v1_qs; + reg_rdata_next[15:10] = clicintv_vsid1_qs; + reg_rdata_next[16] = clicintv_v2_qs; + reg_rdata_next[23:18] = clicintv_vsid2_qs; + reg_rdata_next[24] = clicintv_v3_qs; + reg_rdata_next[31:26] = clicintv_vsid3_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + + // Assertions for Register Interface + `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit)) + +endmodule + +module clicintv_reg_top_intf +#( + parameter int AW = 2, + localparam int DW = 32 +) ( + input logic clk_i, + input logic rst_ni, + REG_BUS.in regbus_slave, + // To HW + output clicintv_reg_pkg::clicintv_reg2hw_t reg2hw, // Write + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + localparam int unsigned STRB_WIDTH = DW/8; + +`include "register_interface/typedef.svh" +`include "register_interface/assign.svh" + + // Define structs for reg_bus + typedef logic [AW-1:0] addr_t; + typedef logic [DW-1:0] data_t; + typedef logic [STRB_WIDTH-1:0] strb_t; + `REG_BUS_TYPEDEF_ALL(reg_bus, addr_t, data_t, strb_t) + + reg_bus_req_t s_reg_req; + reg_bus_rsp_t s_reg_rsp; + + // Assign SV interface to structs + `REG_BUS_ASSIGN_TO_REQ(s_reg_req, regbus_slave) + `REG_BUS_ASSIGN_FROM_RSP(regbus_slave, s_reg_rsp) + + + + clicintv_reg_top #( + .reg_req_t(reg_bus_req_t), + .reg_rsp_t(reg_bus_rsp_t), + .AW(AW) + ) i_regs ( + .clk_i, + .rst_ni, + .reg_req_i(s_reg_req), + .reg_rsp_o(s_reg_rsp), + .reg2hw, // Write + .devmode_i + ); + +endmodule + + diff --git a/src/clicvs_reg_pkg.sv b/src/clicvs_reg_pkg.sv new file mode 100644 index 0000000..ea3f6e7 --- /dev/null +++ b/src/clicvs_reg_pkg.sv @@ -0,0 +1,50 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package clicvs_reg_pkg; + + // Address widths within the block + parameter int BlockAw = 2; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + struct packed { + logic q; + } prio0; + struct packed { + logic q; + } prio1; + struct packed { + logic q; + } prio2; + struct packed { + logic q; + } prio3; + } clicvs_reg2hw_vsprio_reg_t; + + // Register -> HW type + typedef struct packed { + clicvs_reg2hw_vsprio_reg_t vsprio; // [3:0] + } clicvs_reg2hw_t; + + // Register offsets + parameter logic [BlockAw-1:0] CLICVS_VSPRIO_OFFSET = 2'h 0; + + // Register index + typedef enum int { + CLICVS_VSPRIO + } clicvs_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] CLICVS_PERMIT [1] = '{ + 4'b 1111 // index[0] CLICVS_VSPRIO + }; + +endpackage + diff --git a/src/clicvs_reg_top.sv b/src/clicvs_reg_top.sv new file mode 100644 index 0000000..f836899 --- /dev/null +++ b/src/clicvs_reg_top.sv @@ -0,0 +1,297 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + +`include "common_cells/assertions.svh" + +module clicvs_reg_top #( + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter int AW = 2 +) ( + input logic clk_i, + input logic rst_ni, + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + // To HW + output clicvs_reg_pkg::clicvs_reg2hw_t reg2hw, // Write + + + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + + import clicvs_reg_pkg::* ; + + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [BlockAw-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + // Below register interface can be changed + reg_req_t reg_intf_req; + reg_rsp_t reg_intf_rsp; + + + assign reg_intf_req = reg_req_i; + assign reg_rsp_o = reg_intf_rsp; + + + assign reg_we = reg_intf_req.valid & reg_intf_req.write; + assign reg_re = reg_intf_req.valid & ~reg_intf_req.write; + assign reg_addr = reg_intf_req.addr[BlockAw-1:0]; + assign reg_wdata = reg_intf_req.wdata; + assign reg_be = reg_intf_req.wstrb; + assign reg_intf_rsp.rdata = reg_rdata; + assign reg_intf_rsp.error = reg_error; + assign reg_intf_rsp.ready = 1'b1; + + assign reg_rdata = reg_rdata_next ; + assign reg_error = (devmode_i & addrmiss) | wr_err; + + + // Define SW related signals + // Format: __{wd|we|qs} + // or _{wd|we|qs} if field == 1 or 0 + logic vsprio_prio0_qs; + logic vsprio_prio0_wd; + logic vsprio_prio0_we; + logic vsprio_prio1_qs; + logic vsprio_prio1_wd; + logic vsprio_prio1_we; + logic vsprio_prio2_qs; + logic vsprio_prio2_wd; + logic vsprio_prio2_we; + logic vsprio_prio3_qs; + logic vsprio_prio3_wd; + logic vsprio_prio3_we; + + // Register instances + // R[vsprio]: V(False) + + // F[prio0]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_vsprio_prio0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (vsprio_prio0_we), + .wd (vsprio_prio0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.vsprio.prio0.q ), + + // to register interface (read) + .qs (vsprio_prio0_qs) + ); + + + // F[prio1]: 8:8 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_vsprio_prio1 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (vsprio_prio1_we), + .wd (vsprio_prio1_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.vsprio.prio1.q ), + + // to register interface (read) + .qs (vsprio_prio1_qs) + ); + + + // F[prio2]: 16:16 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_vsprio_prio2 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (vsprio_prio2_we), + .wd (vsprio_prio2_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.vsprio.prio2.q ), + + // to register interface (read) + .qs (vsprio_prio2_qs) + ); + + + // F[prio3]: 24:24 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_vsprio_prio3 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (vsprio_prio3_we), + .wd (vsprio_prio3_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.vsprio.prio3.q ), + + // to register interface (read) + .qs (vsprio_prio3_qs) + ); + + + + + logic [0:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == CLICVS_VSPRIO_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[0] & (|(CLICVS_PERMIT[0] & ~reg_be))))); + end + + assign vsprio_prio0_we = addr_hit[0] & reg_we & !reg_error; + assign vsprio_prio0_wd = reg_wdata[0]; + + assign vsprio_prio1_we = addr_hit[0] & reg_we & !reg_error; + assign vsprio_prio1_wd = reg_wdata[8]; + + assign vsprio_prio2_we = addr_hit[0] & reg_we & !reg_error; + assign vsprio_prio2_wd = reg_wdata[16]; + + assign vsprio_prio3_we = addr_hit[0] & reg_we & !reg_error; + assign vsprio_prio3_wd = reg_wdata[24]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = vsprio_prio0_qs; + reg_rdata_next[8] = vsprio_prio1_qs; + reg_rdata_next[16] = vsprio_prio2_qs; + reg_rdata_next[24] = vsprio_prio3_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + + // Assertions for Register Interface + `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit)) + +endmodule + +module clicvs_reg_top_intf +#( + parameter int AW = 2, + localparam int DW = 32 +) ( + input logic clk_i, + input logic rst_ni, + REG_BUS.in regbus_slave, + // To HW + output clicvs_reg_pkg::clicvs_reg2hw_t reg2hw, // Write + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + localparam int unsigned STRB_WIDTH = DW/8; + +`include "register_interface/typedef.svh" +`include "register_interface/assign.svh" + + // Define structs for reg_bus + typedef logic [AW-1:0] addr_t; + typedef logic [DW-1:0] data_t; + typedef logic [STRB_WIDTH-1:0] strb_t; + `REG_BUS_TYPEDEF_ALL(reg_bus, addr_t, data_t, strb_t) + + reg_bus_req_t s_reg_req; + reg_bus_rsp_t s_reg_rsp; + + // Assign SV interface to structs + `REG_BUS_ASSIGN_TO_REQ(s_reg_req, regbus_slave) + `REG_BUS_ASSIGN_FROM_RSP(regbus_slave, s_reg_rsp) + + + + clicvs_reg_top #( + .reg_req_t(reg_bus_req_t), + .reg_rsp_t(reg_bus_rsp_t), + .AW(AW) + ) i_regs ( + .clk_i, + .rst_ni, + .reg_req_i(s_reg_req), + .reg_rsp_o(s_reg_rsp), + .reg2hw, // Write + .devmode_i + ); + +endmodule + + diff --git a/src/gen/Makefile b/src/gen/Makefile index 991694f..a12ee74 100644 --- a/src/gen/Makefile +++ b/src/gen/Makefile @@ -19,9 +19,18 @@ REGTOOL = regtool.py +SRCS = clicvs_reg_pkg.sv clicvs_reg_top.sv clicintv_reg_pkg.sv clicintv_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv mclic_reg_pkg.sv mclic_reg_top.sv +HDRS = clic.h clicint.h clicintv.h clicvs.h + all: headers srcs -srcs: clicint_reg_pkg.sv clicint_reg_top.sv mclic_reg_pkg.sv mclic_reg_top.sv +srcs: $(SRCS) + +clicvs_reg_pkg.sv clicvs_reg_top.sv: clicvs.hjson + $(REGTOOL) -r $< -t . + +clicintv_reg_pkg.sv clicintv_reg_top.sv: clicintv.hjson + $(REGTOOL) -r $< -t . clicint_reg_pkg.sv clicint_reg_top.sv: clicint.hjson $(REGTOOL) -r $< -t . @@ -29,7 +38,7 @@ clicint_reg_pkg.sv clicint_reg_top.sv: clicint.hjson mclic_reg_pkg.sv mclic_reg_top.sv: mclic.hjson $(REGTOOL) -r $< -t . -headers: clic.h clicint.h +headers: $(HDRS) clic.h: mclic.hjson $(REGTOOL) --cdefines mclic.hjson > $@ @@ -37,11 +46,17 @@ clic.h: mclic.hjson clicint.h: clicint.hjson $(REGTOOL) --cdefines clicint.hjson > $@ +clicintv.h: clicintv.hjson + $(REGTOOL) --cdefines clicintv.hjson > $@ + +clicvs.h: clicvs.hjson + $(REGTOOL) --cdefines clicvs.hjson > $@ + .PHONY: install install: - cp mclic_reg_pkg.sv mclic_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv .. + cp $(SRCS) .. .PHONY: clean clean: - rm clic.h mclic_reg_pkg.sv mclic_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv + rm $(HDRS) $(SRCS) diff --git a/src/gen/clicintv.hjson b/src/gen/clicintv.hjson new file mode 100644 index 0000000..f31c500 --- /dev/null +++ b/src/gen/clicintv.hjson @@ -0,0 +1,39 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this 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. + +// SPDX-License-Identifier: Apache-2.0 + +// CLIC virtual supervisor interrupt register +{ + name: "CLICINTV", + clock_primary: "clk_i", + bus_interfaces: [ + { protocol: "reg_iface", direction: "device" } + ], + + regwidth: "32", + registers: [ + { name: "CLICINTV", + desc: "CLIC interrupt virtualization", + swaccess: "rw", + hwaccess: "hro", + fields: [ + { bits: "31:26", name: "VSID3", desc: "interrupt VS id" }, + { bits: "24", name: "V3", desc: "interrupt delegated to VS-mode"}, + { bits: "23:18", name: "VSID2", desc: "interrupt VS id" }, + { bits: "16", name: "V2", desc: "interrupt delegated to VS-mode"}, + { bits: "15:10", name: "VSID1", desc: "interrupt VS id" }, + { bits: "8", name: "V1", desc: "interrupt delegated to VS-mode"}, + { bits: "7:2", name: "VSID0", desc: "interrupt VS id" }, + { bits: "0", name: "V0", desc: "interrupt delegated to VS-mode"}, + ], + } + ] +} diff --git a/src/gen/clicvs.hjson b/src/gen/clicvs.hjson new file mode 100644 index 0000000..fccc122 --- /dev/null +++ b/src/gen/clicvs.hjson @@ -0,0 +1,35 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this 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. + +// SPDX-License-Identifier: Apache-2.0 + +// CLIC virtual supervisor configuration register +{ + name: "CLICVS", + clock_primary: "clk_i", + bus_interfaces: [ + { protocol: "reg_iface", direction: "device" } + ], + + regwidth: "32", + registers: [ + { name: "vsprio", + desc: "CLIC virtual supervisor priority", + swaccess: "rw", + hwaccess: "hro", + fields: [ + { bits: "24", name: "prio3", desc: "VS3 priority" }, + { bits: "16", name: "prio2", desc: "VS2 priority" }, + { bits: "8", name: "prio1", desc: "VS1 priority" }, + { bits: "0", name: "prio0", desc: "VS0 priority" }, + ], + } + ] +} diff --git a/src/gen/mclic.hjson b/src/gen/mclic.hjson index bd1b79a..c6bde00 100644 --- a/src/gen/mclic.hjson +++ b/src/gen/mclic.hjson @@ -34,6 +34,14 @@ { bits: "3:0", name: "mnlbits", desc: "number of interrupt level bits in machine mode" }, ], }, + { name: "CLICMNXTICONF", + desc: "CLIC enable mnxti irq forwarding logic", + swaccess: "rw", + hwaccess: "hro", + fields: [ + { bits: "0" } + ], + }, ] } diff --git a/src/mclic_reg_pkg.sv b/src/mclic_reg_pkg.sv index a194e30..f987079 100644 --- a/src/mclic_reg_pkg.sv +++ b/src/mclic_reg_pkg.sv @@ -7,7 +7,7 @@ package mclic_reg_pkg; // Address widths within the block - parameter int BlockAw = 2; + parameter int BlockAw = 3; //////////////////////////// // Typedefs for registers // @@ -28,22 +28,30 @@ package mclic_reg_pkg; } unlbits; } mclic_reg2hw_mcliccfg_reg_t; + typedef struct packed { + logic q; + } mclic_reg2hw_clicmnxticonf_reg_t; + // Register -> HW type typedef struct packed { - mclic_reg2hw_mcliccfg_reg_t mcliccfg; // [13:0] + mclic_reg2hw_mcliccfg_reg_t mcliccfg; // [14:1] + mclic_reg2hw_clicmnxticonf_reg_t clicmnxticonf; // [0:0] } mclic_reg2hw_t; // Register offsets - parameter logic [BlockAw-1:0] MCLIC_MCLICCFG_OFFSET = 2'h 0; + parameter logic [BlockAw-1:0] MCLIC_MCLICCFG_OFFSET = 3'h 0; + parameter logic [BlockAw-1:0] MCLIC_CLICMNXTICONF_OFFSET = 3'h 4; // Register index typedef enum int { - MCLIC_MCLICCFG + MCLIC_MCLICCFG, + MCLIC_CLICMNXTICONF } mclic_id_e; // Register width information to check illegal writes - parameter logic [3:0] MCLIC_PERMIT [1] = '{ - 4'b 1111 // index[0] MCLIC_MCLICCFG + parameter logic [3:0] MCLIC_PERMIT [2] = '{ + 4'b 1111, // index[0] MCLIC_MCLICCFG + 4'b 0001 // index[1] MCLIC_CLICMNXTICONF }; endpackage diff --git a/src/mclic_reg_top.sv b/src/mclic_reg_top.sv index 93560ed..d52cdf7 100644 --- a/src/mclic_reg_top.sv +++ b/src/mclic_reg_top.sv @@ -10,7 +10,7 @@ module mclic_reg_top #( parameter type reg_req_t = logic, parameter type reg_rsp_t = logic, - parameter int AW = 2 + parameter int AW = 3 ) ( input logic clk_i, input logic rst_ni, @@ -80,6 +80,9 @@ module mclic_reg_top #( logic [3:0] mcliccfg_unlbits_wd; logic mcliccfg_unlbits_we; logic [3:0] mcliccfg_reserved_qs; + logic clicmnxticonf_qs; + logic clicmnxticonf_wd; + logic clicmnxticonf_we; // Register instances // R[mcliccfg]: V(False) @@ -193,12 +196,40 @@ module mclic_reg_top #( assign mcliccfg_reserved_qs = 4'h0; + // R[clicmnxticonf]: V(False) + + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_clicmnxticonf ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicmnxticonf_we), + .wd (clicmnxticonf_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicmnxticonf.q ), + + // to register interface (read) + .qs (clicmnxticonf_qs) + ); - logic [0:0] addr_hit; + + + logic [1:0] addr_hit; always_comb begin addr_hit = '0; addr_hit[0] = (reg_addr == MCLIC_MCLICCFG_OFFSET); + addr_hit[1] = (reg_addr == MCLIC_CLICMNXTICONF_OFFSET); end assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; @@ -206,7 +237,8 @@ module mclic_reg_top #( // Check sub-word write is permitted always_comb begin wr_err = (reg_we & - ((addr_hit[0] & (|(MCLIC_PERMIT[0] & ~reg_be))))); + ((addr_hit[0] & (|(MCLIC_PERMIT[0] & ~reg_be))) | + (addr_hit[1] & (|(MCLIC_PERMIT[1] & ~reg_be))))); end assign mcliccfg_mnlbits_we = addr_hit[0] & reg_we & !reg_error; @@ -221,6 +253,9 @@ module mclic_reg_top #( assign mcliccfg_unlbits_we = addr_hit[0] & reg_we & !reg_error; assign mcliccfg_unlbits_wd = reg_wdata[27:24]; + assign clicmnxticonf_we = addr_hit[1] & reg_we & !reg_error; + assign clicmnxticonf_wd = reg_wdata[0]; + // Read data return always_comb begin reg_rdata_next = '0; @@ -233,6 +268,10 @@ module mclic_reg_top #( reg_rdata_next[31:28] = mcliccfg_reserved_qs; end + addr_hit[1]: begin + reg_rdata_next[0] = clicmnxticonf_qs; + end + default: begin reg_rdata_next = '1; end @@ -255,7 +294,7 @@ endmodule module mclic_reg_top_intf #( - parameter int AW = 2, + parameter int AW = 3, localparam int DW = 32 ) ( input logic clk_i, diff --git a/src_files.yml b/src_files.yml index 2bb5a92..690f9d5 100644 --- a/src_files.yml +++ b/src_files.yml @@ -18,6 +18,10 @@ clic: files: - src/clicint_reg_pkg.sv - src/clicint_reg_top.sv + - src/clicintv_reg_pkg.sv + - src/clicintv_reg_top.sv + - src/clicvs_reg_pkg.sv + - src/clicvs_reg_top.sv - src/mclic_reg_pkg.sv - src/mclic_reg_top.sv - src/clic_reg_adapter.sv