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 bbdd7ce..a462249 100644 --- a/src/clic.sv +++ b/src/clic.sv @@ -17,15 +17,26 @@ `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 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 +49,95 @@ 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] 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; @@ -100,9 +164,12 @@ 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), + .MAX_VSCTXTS (MAX_VSCTXTS), + .PrioWidth (INTCTLBITS), + .ModeWidth (2), + .VsidWidth (VSID_W), + .VsprioWidth (VsprioWidth) ) i_clic_target ( .clk_i, .rst_ni, @@ -110,9 +177,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,6 +193,9 @@ 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 @@ -150,14 +225,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,20 +259,118 @@ 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]), - // interrupt control and status register - // 0x9000 - 0xcfff (supervisor mode) - // mirror + .devmode_i (1'b1) + ); + + end + + 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 + assign clicintv_reg2hw = '0; + // assign clicintv_hw2reg = '0; + assign reg_v_req = '0; + assign reg_v_rsp = '0; + assign v_addr = '0; + assign reg_all_v_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 + unique case(reg_req_i.addr[ADDR_W-1:0]) inside MCLICCFG_START: begin reg_mclic_req = reg_req_i; reg_rsp_o = reg_mclic_rsp; @@ -215,10 +388,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 +406,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 +455,49 @@ 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 + case(reg_req_i.addr[ADDR_W-1:0]) inside + `VSCLICCFG_START(i): begin + // inaccesible (all zero) + reg_rsp_o.rdata = '0; + reg_rsp_o.error = '0; + reg_rsp_o.ready = 1'b1; + end + [`VSCLICINT_START(i):`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 + endcase // case (reg_req_i.addr) + 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,9 +507,18 @@ 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), @@ -271,13 +533,10 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( always_comb begin // Saturate nlbits if nlbits > clicintctlbits (nlbits > 0 && nlbits <= 8) mnlbits = INTCTLBITS; - if (mnlbits <= INTCTLBITS) + if (mclic_reg2hw.mcliccfg.mnlbits.q <= INTCTLBITS) 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 +582,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..c1d901e 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,8 +29,17 @@ 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, @@ -50,4 +62,22 @@ 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 + endmodule // clic_reg_adapter diff --git a/src/clic_target.sv b/src/clic_target.sv index e680049..9d0e890 100644 --- a/src/clic_target.sv +++ b/src/clic_target.sv @@ -29,9 +29,12 @@ `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 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 +45,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,6 +61,9 @@ 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 @@ -61,13 +72,22 @@ module clic_target #( // 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; + logic [2**(NumLevels+1)-2:0][SrcWidth-1:0] id_tree; + prio_t [2**(NumLevels+1)-2:0] max_tree; for (genvar level = 0; level < NumLevels+1; level++) begin : gen_tree // @@ -95,13 +115,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 +147,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,8 +168,9 @@ 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; // the results can be found at the tree root // TODO: remove useless inequality comparison @@ -164,7 +191,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; @@ -178,7 +206,8 @@ module clic_target #( if (irq_root_valid) begin irq_id_d = irq_root_id; irq_max_d = max_tree[0]; - irq_mode_d = mode_tree[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 @@ -187,7 +216,8 @@ module clic_target #( irq_valid_d = 1'b1; irq_id_d = irq_id_q; irq_max_d = irq_max_q; - irq_mode_d = irq_mode_q; + vsid_max_d = vsid_max_q; + shv_max_d = shv_max_q; // 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 +255,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 +273,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/clicint_reg_top.sv b/src/clicint_reg_top.sv index 0ec8ae1..e85f663 100644 --- a/src/clicint_reg_top.sv +++ b/src/clicint_reg_top.sv @@ -8,12 +8,12 @@ `include "common_cells/assertions.svh" module clicint_reg_top #( - parameter type reg_req_t = logic, - parameter type reg_rsp_t = logic, - parameter int AW = 2 + 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 clk_i, + input rst_ni, input reg_req_t reg_req_i, output reg_rsp_t reg_rsp_o, // To HW @@ -33,7 +33,7 @@ module clicint_reg_top #( // register signals logic reg_we; logic reg_re; - logic [BlockAw-1:0] reg_addr; + logic [AW-1:0] reg_addr; logic [DW-1:0] reg_wdata; logic [DBW-1:0] reg_be; logic [DW-1:0] reg_rdata; @@ -54,7 +54,7 @@ module clicint_reg_top #( 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_addr = reg_intf_req.addr; assign reg_wdata = reg_intf_req.wdata; assign reg_be = reg_intf_req.wstrb; assign reg_intf_rsp.rdata = reg_rdata; @@ -116,7 +116,7 @@ module clicint_reg_top #( ); - // F[ie]: 7:7 + // F[ie]: 8:8 prim_subreg #( .DW (1), .SWACCESS("RW"), @@ -266,7 +266,7 @@ module clicint_reg_top #( assign clicint_ip_wd = reg_wdata[0]; assign clicint_ie_we = addr_hit[0] & reg_we & !reg_error; - assign clicint_ie_wd = reg_wdata[7]; + assign clicint_ie_wd = reg_wdata[8]; assign clicint_attr_shv_we = addr_hit[0] & reg_we & !reg_error; assign clicint_attr_shv_wd = reg_wdata[16]; @@ -286,7 +286,7 @@ module clicint_reg_top #( unique case (1'b1) addr_hit[0]: begin reg_rdata_next[0] = clicint_ip_qs; - reg_rdata_next[7] = clicint_ie_qs; + reg_rdata_next[8] = clicint_ie_qs; reg_rdata_next[16] = clicint_attr_shv_qs; reg_rdata_next[18:17] = clicint_attr_trig_qs; reg_rdata_next[23:22] = clicint_attr_mode_qs; @@ -312,55 +312,3 @@ module clicint_reg_top #( `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit)) endmodule - -module clicint_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 clicint_reg_pkg::clicint_reg2hw_t reg2hw, // Write - input clicint_reg_pkg::clicint_hw2reg_t hw2reg, // Read - // 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) - - - - clicint_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 - .hw2reg, // Read - .devmode_i - ); - -endmodule - - 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..0534036 --- /dev/null +++ b/src/clicintv_reg_top.sv @@ -0,0 +1,379 @@ +// 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 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/clicint.hjson b/src/gen/clicint.hjson index f2fa0de..74d9370 100644 --- a/src/gen/clicint.hjson +++ b/src/gen/clicint.hjson @@ -31,7 +31,7 @@ { bits: "18:17", name: "ATTR_TRIG", desc: "specify trigger type for this interrupt" }, { bits: "16", name: "ATTR_SHV", desc: "enable hardware vectoring for this interrupt" }, - { bits: "7", name: "IE", desc: "interrupt enable for interrupt" }, + { bits: "8", name: "IE", desc: "interrupt enable for interrupt" }, { bits: "0", name: "IP", desc: "interrupt pending for interrupt", hwaccess: "hrw" }, ], 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_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