From 284c539d1648ae88960141be01ccce76b9ead1e5 Mon Sep 17 00:00:00 2001 From: Enrico Zelioli Date: Tue, 23 May 2023 14:47:32 +0200 Subject: [PATCH 1/9] [ADD] ignore temp files --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) 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 From 491258b97d3bf3157ac67be5ed81667659de7f4c Mon Sep 17 00:00:00 2001 From: Enrico Zelioli Date: Fri, 26 May 2023 10:00:36 +0200 Subject: [PATCH 2/9] [FIX] Register generation --- src/gen/Makefile | 2 +- src/gen/clicint.hjson | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gen/Makefile b/src/gen/Makefile index 991694f..94e6c2d 100644 --- a/src/gen/Makefile +++ b/src/gen/Makefile @@ -43,5 +43,5 @@ install: .PHONY: clean clean: - rm clic.h mclic_reg_pkg.sv mclic_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv + rm clic.h clicint.h mclic_reg_pkg.sv mclic_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv 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" }, ], From b5058554068b9dfddc92dd9ee88389be6612bb07 Mon Sep 17 00:00:00 2001 From: Enrico Zelioli Date: Mon, 5 Jun 2023 18:09:49 +0200 Subject: [PATCH 3/9] [ADD] Interrupt virtualization support --- src/clic.sv | 189 ++++++++++++++++++++++++++++++++++++---- src/clic_reg_adapter.sv | 9 +- src/clic_target.sv | 14 ++- src/clicintv_reg_pkg.sv | 44 ++++++++++ src/clicintv_reg_top.sv | 181 ++++++++++++++++++++++++++++++++++++++ src/gen/Makefile | 14 ++- src/gen/clicintv.hjson | 34 ++++++++ 7 files changed, 463 insertions(+), 22 deletions(-) create mode 100644 src/clicintv_reg_pkg.sv create mode 100644 src/clicintv_reg_top.sv create mode 100644 src/gen/clicintv.hjson diff --git a/src/clic.sv b/src/clic.sv index bbdd7ce..0c83571 100644 --- a/src/clic.sv +++ b/src/clic.sv @@ -17,15 +17,20 @@ `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::*; #( 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, // vCLIC enable + 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) // do not edit below, these are derived - localparam int SRC_W = $clog2(N_SOURCE) + localparam int SRC_W = $clog2(N_SOURCE), + localparam int MAX_VSCTXTS = 64 )( input logic clk_i, input logic rst_ni, @@ -44,6 +49,8 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( output logic [7:0] irq_level_o, output logic irq_shv_o, output logic [1:0] irq_priv_o, + output logic [5:0] irq_vsid_o, // up to 64 VS contexts + output logic irq_v_o, output logic irq_kill_req_o, input logic irq_kill_ack_i ); @@ -51,29 +58,52 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( if (USCLIC) $fatal(1, "usclic mode is not supported"); + if (N_VSCTXTS > MAX_VSCTXTS) + $fatal(1, "vsclic extension supports up to 64 virtual contexts"); + + // CLIC internal address size + localparam int unsigned ADDR_W = $clog2(MAX_VSCTXTS * 8 * 1024); // 19 + 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; + // Each privilege mode address space is aligned to a 8KiB 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] MCLICINTV_START = 'h05000; + localparam logic [ADDR_W-1:0] MCLICINTV_END = 'h05fff; + + 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; + + `define VSCLICCFG_START(i) ('h08000 * (i + 2)) + `define VSCLICINT_START(i) ('h08000 * (i + 2) + 'h01000) + `define VSCLICINT_END(i) ('h08000 * (i + 2) + 'h04fff) + `define VSCLICINTV_START(i) ('h08000 * (i + 2) + 'h05000) + `define VSCLICINTV_END(i) ('h08000 * (i + 2) + 'h05fff) 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-1:0] clicintv_reg2hw; + // clicintv_hw2reg_t [N_SOURCE-1:0] clicintv_hw2reg; + logic [7:0] intctl [N_SOURCE]; logic [7:0] irq_max; logic [1:0] intmode [N_SOURCE]; logic [1:0] irq_mode; + logic [5:0] vsid [N_SOURCE]; // Per-IRQ Virtual Supervisor (VS) ID + logic intv [N_SOURCE]; // Per-IRQ virtualization bit + logic [N_SOURCE-1:0] le; // 0: level-sensitive 1: edge-sensitive logic [N_SOURCE-1:0] ip; logic [N_SOURCE-1:0] ie; @@ -113,6 +143,7 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( .prio_i (intctl), .mode_i (intmode), + .intv_i (intv), .claim_o (claim), @@ -150,14 +181,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,6 +215,51 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( ); end + // 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-1:0] reg_v_req; + reg_rsp_t [N_SOURCE-1:0] reg_v_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; 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 + end else begin + assign clicintv_reg2hw = '0; + // assign clicintv_hw2reg = '0; + assign v_addr = '0; + assign reg_all_v_rsp = '0; + end + // configuration registers // 0x8000 (supervisor mode) @@ -195,9 +271,10 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( always_comb begin : clic_addr_decode reg_mclic_req = '0; reg_all_int_req = '0; + reg_all_v_req = '0; reg_rsp_o = '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; @@ -207,6 +284,18 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( reg_all_int_req.addr = reg_req_i.addr - MCLICINT_START; reg_rsp_o = reg_all_int_rsp; end + [MCLICINTV_START:MCLICINTV_END]: begin + if (VSCLIC) begin + reg_all_v_req = reg_req_i; + reg_all_v_req.addr = reg_req_i.addr - MCLICINTV_START; + reg_rsp_o = reg_all_v_rsp; + end else begin + // VSCLIC disabled + reg_rsp_o.rdata = '0; + reg_rsp_o.error = '0; + reg_rsp_o.ready = 1'b1; + end + end SCLICCFG_START: begin if (SSCLIC) begin reg_mclic_req = reg_req_i; @@ -216,7 +305,7 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( [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 + if (intmode[reg_all_int_req.addr[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; // Prevent setting interrupt mode to m-mode . This is currently a @@ -232,6 +321,26 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( end end end + [SCLICINTV_START:SCLICINTV_END]: begin + if (VSCLIC) begin + reg_all_v_req.addr = reg_req_i.addr - SCLICINTV_START; + if (intmode[reg_all_v_req.addr[ADDR_W-1:2]] <= S_MODE) begin + // check whether the irq we want to access is s-mode or lower + reg_all_v_req = reg_req_i; + reg_rsp_o = reg_all_v_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 else begin + // VSCLIC disabled + 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,6 +348,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 = 0; i < N_VSCTXTS; i++) begin + case(reg_req_i.addr[ADDR_W-1:0]) inside + // TODO: whether / how to grant access to MCLICCFG register + `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 + reg_all_int_req.addr = reg_req_i.addr - `VSCLICINT_START(i); + if ((intmode[reg_all_int_req.addr[ADDR_W-1:2]] == S_MODE) && + (intv[reg_all_int_req.addr[ADDR_W-1:2]]) && + (vsid[reg_all_int_req.addr[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; + // 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 + // TODO: whether / how to grant access to CLICINTV registers + [`VSCLICINTV_START(i):`VSCLICINTV_END(i)]: begin + // inaccesible (all zero) + reg_rsp_o.rdata = '0; + reg_rsp_o.error = '0; + reg_rsp_o.ready = 1'b1; + end + endcase // case (reg_req_i.addr) + end + end end // adapter @@ -254,9 +404,14 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( .clicint_reg2hw, .clicint_hw2reg, + .clicintv_reg2hw, + // .clicintv_hw2reg, + .intctl_o (intctl), .intmode_o (intmode), .shv_o (shv), + .vsid_o (vsid), + .intv_o (intv), .ip_sw_o (ip_sw), .ie_o (ie), .le_o (le), @@ -277,6 +432,10 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( // Extract SHV bit for the highest level, highest priority pending interrupt assign irq_shv_o = shv[irq_id_o]; + + // Extract Virtual Supervisor ID for the highest level, highest priority pending interrupt + assign irq_vsid_o = (VSCLIC && (irq_priv_o == S_MODE)) ? vsid[irq_id_o] : '0; + assign irq_v_o = (VSCLIC && (irq_priv_o == S_MODE)) ? intv[irq_id_o] : '0; logic [7:0] irq_level_tmp; @@ -323,10 +482,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..1f0f6f7 100644 --- a/src/clic_reg_adapter.sv +++ b/src/clic_reg_adapter.sv @@ -14,7 +14,7 @@ // 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::*; #( parameter int N_SOURCE = 32, parameter int INTCTLBITS = 8 )( @@ -26,8 +26,13 @@ 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, + input clicintv_reg_pkg::clicintv_reg2hw_t [N_SOURCE-1:0] clicintv_reg2hw, + // output clicintv_reg_pkg::clicintv_hw2reg_t [N_SOURCE-1:0] clicintv_hw2reg, + output logic [7:0] intctl_o [N_SOURCE], output logic [1:0] intmode_o [N_SOURCE], + output logic [5:0] vsid_o [N_SOURCE], // interrupt VS id + output logic intv_o [N_SOURCE], // interrupt virtualization 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, @@ -42,6 +47,8 @@ module clic_reg_adapter import mclic_reg_pkg::*; import clicint_reg_pkg::*; #( for (genvar i = 0; i < N_SOURCE; i++) begin : gen_reghw assign intctl_o[i] = clicint_reg2hw[i].clicint.ctl.q; assign intmode_o[i] = clicint_reg2hw[i].clicint.attr_mode.q; + assign vsid_o[i] = clicintv_reg2hw[i].clicintv.vsid.q; + assign intv_o[i] = clicintv_reg2hw[i].clicintv.v.q; assign shv_o[i] = clicint_reg2hw[i].clicint.attr_shv.q; assign ip_sw_o[i] = clicint_reg2hw[i].clicint.ip.q; assign ie_o[i] = clicint_reg2hw[i].clicint.ie.q; diff --git a/src/clic_target.sv b/src/clic_target.sv index e680049..d0a303f 100644 --- a/src/clic_target.sv +++ b/src/clic_target.sv @@ -45,6 +45,7 @@ module clic_target #( input [PrioWidth-1:0] prio_i [N_SOURCE], input [ModeWidth-1:0] mode_i [N_SOURCE], + input logic intv_i [N_SOURCE], output logic [N_SOURCE-1:0] claim_o, @@ -61,6 +62,10 @@ module clic_target #( // this only works with 2 or more sources `ASSERT_INIT(NumSources_A, N_SOURCE >= 2) + 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); @@ -96,7 +101,11 @@ module clic_target #( 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 mode_tree[Pa] = ((mode_i[offset] == S_MODE) && ~intv_i[offset]) ? 2'b10 : mode_i[offset]; end else begin : gen_tie_off assign is_tree[Pa] = '0; assign id_tree[Pa] = '0; @@ -178,7 +187,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]; + // NOTE: If the interrupt priority was modified (see note above), restore nominal privilege + irq_mode_d = (mode_tree[0] == 2'b10) ? S_MODE : mode_tree[0]; irq_valid_d = 1'b1; irq_state_d = ACK; end diff --git a/src/clicintv_reg_pkg.sv b/src/clicintv_reg_pkg.sv new file mode 100644 index 0000000..c8d6afe --- /dev/null +++ b/src/clicintv_reg_pkg.sv @@ -0,0 +1,44 @@ +// 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 = 0; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + struct packed { + logic q; + } v; + struct packed { + logic [5:0] q; + } vsid; + } clicintv_reg2hw_clicintv_reg_t; + + // Register -> HW type + typedef struct packed { + clicintv_reg2hw_clicintv_reg_t clicintv; // [6:0] + } clicintv_reg2hw_t; + + // Register offsets + parameter logic [BlockAw-1:0] CLICINTV_CLICINTV_OFFSET = 0'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 0001 // 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..9f69bba --- /dev/null +++ b/src/clicintv_reg_top.sv @@ -0,0 +1,181 @@ +// 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 = 0 +) ( + input clk_i, + input 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 = 8; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-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; + 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_v_qs; + logic clicintv_v_wd; + logic clicintv_v_we; + logic [5:0] clicintv_vsid_qs; + logic [5:0] clicintv_vsid_wd; + logic clicintv_vsid_we; + + // Register instances + // R[clicintv]: V(False) + + // F[v]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_clicintv_v ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicintv_v_we), + .wd (clicintv_v_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicintv.v.q ), + + // to register interface (read) + .qs (clicintv_v_qs) + ); + + + // F[vsid]: 7:2 + prim_subreg #( + .DW (6), + .SWACCESS("RW"), + .RESVAL (6'h0) + ) u_clicintv_vsid ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (clicintv_vsid_we), + .wd (clicintv_vsid_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.clicintv.vsid.q ), + + // to register interface (read) + .qs (clicintv_vsid_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_v_we = addr_hit[0] & reg_we & !reg_error; + assign clicintv_v_wd = reg_wdata[0]; + + assign clicintv_vsid_we = addr_hit[0] & reg_we & !reg_error; + assign clicintv_vsid_wd = reg_wdata[7:2]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = clicintv_v_qs; + reg_rdata_next[7:2] = clicintv_vsid_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/gen/Makefile b/src/gen/Makefile index 94e6c2d..47d0da4 100644 --- a/src/gen/Makefile +++ b/src/gen/Makefile @@ -21,7 +21,10 @@ REGTOOL = regtool.py all: headers srcs -srcs: clicint_reg_pkg.sv clicint_reg_top.sv mclic_reg_pkg.sv mclic_reg_top.sv +srcs: clicintv_reg_pkg.sv clicintv_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv mclic_reg_pkg.sv mclic_reg_top.sv + +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 +32,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: clic.h clicint.h clicintv.h clic.h: mclic.hjson $(REGTOOL) --cdefines mclic.hjson > $@ @@ -37,11 +40,14 @@ clic.h: mclic.hjson clicint.h: clicint.hjson $(REGTOOL) --cdefines clicint.hjson > $@ +clicintv.h: clicintv.hjson + $(REGTOOL) --cdefines clicintv.hjson > $@ + .PHONY: install install: - cp mclic_reg_pkg.sv mclic_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv .. + cp mclic_reg_pkg.sv mclic_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv clicintv_reg_pkg.sv clicintv_reg_top.sv .. .PHONY: clean clean: - rm clic.h clicint.h mclic_reg_pkg.sv mclic_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv + rm clic.h clicint.h clicintv.h mclic_reg_pkg.sv mclic_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv clicintv_reg_pkg.sv clicintv_reg_top.sv diff --git a/src/gen/clicintv.hjson b/src/gen/clicintv.hjson new file mode 100644 index 0000000..daaf5fd --- /dev/null +++ b/src/gen/clicintv.hjson @@ -0,0 +1,34 @@ +// 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: "8", + registers: [ + { name: "CLICINTV", + desc: "CLIC interrupt virtualization", + swaccess: "rw", + hwaccess: "hro", + fields: [ + { bits: "7:2", name: "VSID", desc: "interrupt VS id" }, + // { bits: "1", name: "reserved", desc: "reserved for future use"}, + { bits: "0", name: "V", desc: "interrupt delegated to VS-mode"}, + ], + } + ] +} From e56bcd05dd04c0aed3e6b538f9a40ab0d4f78d3a Mon Sep 17 00:00:00 2001 From: Enrico Zelioli Date: Tue, 4 Jul 2023 10:33:27 +0200 Subject: [PATCH 4/9] [FIX] Config register access from S/VS mode --- src/clic.sv | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/clic.sv b/src/clic.sv index 0c83571..6a8ecf1 100644 --- a/src/clic.sv +++ b/src/clic.sv @@ -268,12 +268,18 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ // mirror // 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_all_int_req = '0; reg_all_v_req = '0; reg_rsp_o = '0; + addr_tmp = '0; + unique case(reg_req_i.addr[ADDR_W-1:0]) inside MCLICCFG_START: begin reg_mclic_req = reg_req_i; @@ -304,10 +310,11 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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[ADDR_W-1: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 @@ -323,10 +330,11 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ end [SCLICINTV_START:SCLICINTV_END]: begin if (VSCLIC) begin - reg_all_v_req.addr = reg_req_i.addr - SCLICINTV_START; - if (intmode[reg_all_v_req.addr[ADDR_W-1:2]] <= S_MODE) begin + addr_tmp = reg_req_i.addr[ADDR_W-1:0] - SCLICINTV_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_v_req = reg_req_i; + reg_all_v_req.addr = addr_tmp; reg_rsp_o = reg_all_v_rsp; end else begin // inaccesible (all zero) @@ -361,12 +369,13 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ reg_rsp_o.ready = 1'b1; end [`VSCLICINT_START(i):`VSCLICINT_END(i)]: begin - reg_all_int_req.addr = reg_req_i.addr - `VSCLICINT_START(i); - if ((intmode[reg_all_int_req.addr[ADDR_W-1:2]] == S_MODE) && - (intv[reg_all_int_req.addr[ADDR_W-1:2]]) && - (vsid[reg_all_int_req.addr[ADDR_W-1:2]] == 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 From 4e241b09d8834781b6b7be498948af716358ac7d Mon Sep 17 00:00:00 2001 From: Enrico Zelioli Date: Thu, 10 Aug 2023 13:13:20 +0200 Subject: [PATCH 5/9] [FIX] Update clicint register layout --- src/clicint_reg_top.sv | 72 ++++++------------------------------------ 1 file changed, 10 insertions(+), 62 deletions(-) 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 - - From ee923bfca3a85886186491dd01e6396a741bb0c2 Mon Sep 17 00:00:00 2001 From: Enrico Zelioli Date: Thu, 10 Aug 2023 13:36:39 +0200 Subject: [PATCH 6/9] [FIX] CLICINTV register layout update --- Bender.yml | 2 + src/clic.sv | 71 +++++------ src/clic_reg_adapter.sv | 16 ++- src/clicintv_reg_pkg.sv | 30 ++++- src/clicintv_reg_top.sv | 262 +++++++++++++++++++++++++++++++++++----- src/gen/clicintv.hjson | 13 +- src_files.yml | 2 + 7 files changed, 308 insertions(+), 88 deletions(-) diff --git a/Bender.yml b/Bender.yml index 18d4c8e..04e624e 100644 --- a/Bender.yml +++ b/Bender.yml @@ -25,6 +25,8 @@ dependencies: sources: - src/clicint_reg_pkg.sv - src/clicint_reg_top.sv + - src/clicintv_reg_pkg.sv + - src/clicintv_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 6a8ecf1..e27ba2d 100644 --- a/src/clic.sv +++ b/src/clic.sv @@ -72,8 +72,6 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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] MCLICINTV_START = 'h05000; - localparam logic [ADDR_W-1:0] MCLICINTV_END = 'h05fff; localparam logic [ADDR_W-1:0] SCLICCFG_START = 'h08000; localparam logic [ADDR_W-1:0] SCLICINT_START = 'h09000; @@ -81,19 +79,18 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ localparam logic [ADDR_W-1:0] SCLICINTV_START = 'h0d000; localparam logic [ADDR_W-1:0] SCLICINTV_END = 'h0dfff; - `define VSCLICCFG_START(i) ('h08000 * (i + 2)) - `define VSCLICINT_START(i) ('h08000 * (i + 2) + 'h01000) - `define VSCLICINT_END(i) ('h08000 * (i + 2) + 'h04fff) - `define VSCLICINTV_START(i) ('h08000 * (i + 2) + 'h05000) - `define VSCLICINTV_END(i) ('h08000 * (i + 2) + 'h05fff) + // 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-1:0] clicintv_reg2hw; - // clicintv_hw2reg_t [N_SOURCE-1:0] clicintv_hw2reg; + clicintv_reg2hw_t [(N_SOURCE/4)-1:0] clicintv_reg2hw; + // clicintv_hw2reg_t [(N_SOURCE/4)-1:0] clicintv_hw2reg; // Not needed logic [7:0] intctl [N_SOURCE]; logic [7:0] irq_max; @@ -221,8 +218,8 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ reg_rsp_t reg_all_v_rsp; logic [ADDR_W-1:0] v_addr; - reg_req_t [N_SOURCE-1:0] reg_v_req; - reg_rsp_t [N_SOURCE-1:0] reg_v_rsp; + reg_req_t [(N_SOURCE/4)-1:0] reg_v_req; + reg_rsp_t [(N_SOURCE/4)-1:0] reg_v_rsp; if (VSCLIC) begin @@ -236,7 +233,7 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ reg_all_v_rsp = reg_v_rsp[v_addr]; end - for (genvar i = 0; i < N_SOURCE; i++) begin : gen_clic_intv + 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) @@ -290,18 +287,6 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ reg_all_int_req.addr = reg_req_i.addr - MCLICINT_START; reg_rsp_o = reg_all_int_rsp; end - [MCLICINTV_START:MCLICINTV_END]: begin - if (VSCLIC) begin - reg_all_v_req = reg_req_i; - reg_all_v_req.addr = reg_req_i.addr - MCLICINTV_START; - reg_rsp_o = reg_all_v_rsp; - end else begin - // VSCLIC disabled - reg_rsp_o.rdata = '0; - reg_rsp_o.error = '0; - reg_rsp_o.ready = 1'b1; - end - end SCLICCFG_START: begin if (SSCLIC) begin reg_mclic_req = reg_req_i; @@ -331,16 +316,25 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ [SCLICINTV_START:SCLICINTV_END]: begin if (VSCLIC) begin addr_tmp = reg_req_i.addr[ADDR_W-1:0] - SCLICINTV_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_v_req = reg_req_i; - reg_all_v_req.addr = addr_tmp; - reg_rsp_o = reg_all_v_rsp; - end else begin - // inaccesible (all zero) - reg_rsp_o.rdata = '0; - reg_rsp_o.error = '0; - reg_rsp_o.ready = 1'b1; + 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 @@ -359,7 +353,7 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ // Match VS address space if (VSCLIC) begin - for (int i = 0; i < N_VSCTXTS; i++) begin + for (int i = 1; i <= N_VSCTXTS; i++) begin case(reg_req_i.addr[ADDR_W-1:0]) inside // TODO: whether / how to grant access to MCLICCFG register `VSCLICCFG_START(i): begin @@ -388,13 +382,6 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ reg_rsp_o.ready = 1'b1; end end - // TODO: whether / how to grant access to CLICINTV registers - [`VSCLICINTV_START(i):`VSCLICINTV_END(i)]: begin - // inaccesible (all zero) - reg_rsp_o.rdata = '0; - reg_rsp_o.error = '0; - reg_rsp_o.ready = 1'b1; - end endcase // case (reg_req_i.addr) end end diff --git a/src/clic_reg_adapter.sv b/src/clic_reg_adapter.sv index 1f0f6f7..ae0f803 100644 --- a/src/clic_reg_adapter.sv +++ b/src/clic_reg_adapter.sv @@ -26,8 +26,8 @@ module clic_reg_adapter import mclic_reg_pkg::*; import clicint_reg_pkg::*; impo 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, - input clicintv_reg_pkg::clicintv_reg2hw_t [N_SOURCE-1:0] clicintv_reg2hw, - // output clicintv_reg_pkg::clicintv_hw2reg_t [N_SOURCE-1:0] clicintv_hw2reg, + 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, output logic [7:0] intctl_o [N_SOURCE], output logic [1:0] intmode_o [N_SOURCE], @@ -47,8 +47,6 @@ module clic_reg_adapter import mclic_reg_pkg::*; import clicint_reg_pkg::*; impo for (genvar i = 0; i < N_SOURCE; i++) begin : gen_reghw assign intctl_o[i] = clicint_reg2hw[i].clicint.ctl.q; assign intmode_o[i] = clicint_reg2hw[i].clicint.attr_mode.q; - assign vsid_o[i] = clicintv_reg2hw[i].clicintv.vsid.q; - assign intv_o[i] = clicintv_reg2hw[i].clicintv.v.q; assign shv_o[i] = clicint_reg2hw[i].clicint.attr_shv.q; assign ip_sw_o[i] = clicint_reg2hw[i].clicint.ip.q; assign ie_o[i] = clicint_reg2hw[i].clicint.ie.q; @@ -57,4 +55,14 @@ module clic_reg_adapter import mclic_reg_pkg::*; import clicint_reg_pkg::*; impo 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 endmodule // clic_reg_adapter diff --git a/src/clicintv_reg_pkg.sv b/src/clicintv_reg_pkg.sv index c8d6afe..e59878e 100644 --- a/src/clicintv_reg_pkg.sv +++ b/src/clicintv_reg_pkg.sv @@ -7,7 +7,7 @@ package clicintv_reg_pkg; // Address widths within the block - parameter int BlockAw = 0; + parameter int BlockAw = 2; //////////////////////////// // Typedefs for registers // @@ -16,19 +16,37 @@ package clicintv_reg_pkg; typedef struct packed { struct packed { logic q; - } v; + } v0; struct packed { logic [5:0] q; - } vsid; + } 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; // [6:0] + clicintv_reg2hw_clicintv_reg_t clicintv; // [27:0] } clicintv_reg2hw_t; // Register offsets - parameter logic [BlockAw-1:0] CLICINTV_CLICINTV_OFFSET = 0'h 0; + parameter logic [BlockAw-1:0] CLICINTV_CLICINTV_OFFSET = 2'h 0; // Register index typedef enum int { @@ -37,7 +55,7 @@ package clicintv_reg_pkg; // Register width information to check illegal writes parameter logic [3:0] CLICINTV_PERMIT [1] = '{ - 4'b 0001 // index[0] CLICINTV_CLICINTV + 4'b 1111 // index[0] CLICINTV_CLICINTV }; endpackage diff --git a/src/clicintv_reg_top.sv b/src/clicintv_reg_top.sv index 9f69bba..0534036 100644 --- a/src/clicintv_reg_top.sv +++ b/src/clicintv_reg_top.sv @@ -8,12 +8,12 @@ `include "common_cells/assertions.svh" module clicintv_reg_top #( - parameter type reg_req_t = logic, - parameter type reg_rsp_t = logic, - parameter int AW = 0 + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter int AW = 2 ) ( - input clk_i, - input rst_ni, + input logic clk_i, + input logic rst_ni, input reg_req_t reg_req_i, output reg_rsp_t reg_rsp_o, // To HW @@ -26,13 +26,13 @@ module clicintv_reg_top #( import clicintv_reg_pkg::* ; - localparam int DW = 8; + localparam int DW = 32; localparam int DBW = DW/8; // Byte Width // register signals logic reg_we; logic reg_re; - logic [AW-1:0] reg_addr; + logic [BlockAw-1:0] reg_addr; logic [DW-1:0] reg_wdata; logic [DBW-1:0] reg_be; logic [DW-1:0] reg_rdata; @@ -53,7 +53,7 @@ module clicintv_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; + 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; @@ -67,28 +67,46 @@ module clicintv_reg_top #( // Define SW related signals // Format: __{wd|we|qs} // or _{wd|we|qs} if field == 1 or 0 - logic clicintv_v_qs; - logic clicintv_v_wd; - logic clicintv_v_we; - logic [5:0] clicintv_vsid_qs; - logic [5:0] clicintv_vsid_wd; - logic clicintv_vsid_we; + 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[v]: 0:0 + // F[v0]: 0:0 prim_subreg #( .DW (1), .SWACCESS("RW"), .RESVAL (1'h0) - ) u_clicintv_v ( + ) u_clicintv_v0 ( .clk_i (clk_i ), .rst_ni (rst_ni ), // from register interface - .we (clicintv_v_we), - .wd (clicintv_v_wd), + .we (clicintv_v0_we), + .wd (clicintv_v0_wd), // from internal hardware .de (1'b0), @@ -96,25 +114,25 @@ module clicintv_reg_top #( // to internal hardware .qe (), - .q (reg2hw.clicintv.v.q ), + .q (reg2hw.clicintv.v0.q ), // to register interface (read) - .qs (clicintv_v_qs) + .qs (clicintv_v0_qs) ); - // F[vsid]: 7:2 + // F[vsid0]: 7:2 prim_subreg #( .DW (6), .SWACCESS("RW"), .RESVAL (6'h0) - ) u_clicintv_vsid ( + ) u_clicintv_vsid0 ( .clk_i (clk_i ), .rst_ni (rst_ni ), // from register interface - .we (clicintv_vsid_we), - .wd (clicintv_vsid_wd), + .we (clicintv_vsid0_we), + .wd (clicintv_vsid0_wd), // from internal hardware .de (1'b0), @@ -122,10 +140,166 @@ module clicintv_reg_top #( // to internal hardware .qe (), - .q (reg2hw.clicintv.vsid.q ), + .q (reg2hw.clicintv.vsid0.q ), // to register interface (read) - .qs (clicintv_vsid_qs) + .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) ); @@ -145,19 +319,43 @@ module clicintv_reg_top #( ((addr_hit[0] & (|(CLICINTV_PERMIT[0] & ~reg_be))))); end - assign clicintv_v_we = addr_hit[0] & reg_we & !reg_error; - assign clicintv_v_wd = reg_wdata[0]; + 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_vsid_we = addr_hit[0] & reg_we & !reg_error; - assign clicintv_vsid_wd = reg_wdata[7:2]; + 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_v_qs; - reg_rdata_next[7:2] = clicintv_vsid_qs; + 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 diff --git a/src/gen/clicintv.hjson b/src/gen/clicintv.hjson index daaf5fd..f31c500 100644 --- a/src/gen/clicintv.hjson +++ b/src/gen/clicintv.hjson @@ -18,16 +18,21 @@ { protocol: "reg_iface", direction: "device" } ], - regwidth: "8", + regwidth: "32", registers: [ { name: "CLICINTV", desc: "CLIC interrupt virtualization", swaccess: "rw", hwaccess: "hro", fields: [ - { bits: "7:2", name: "VSID", desc: "interrupt VS id" }, - // { bits: "1", name: "reserved", desc: "reserved for future use"}, - { bits: "0", name: "V", desc: "interrupt delegated to VS-mode"}, + { 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_files.yml b/src_files.yml index 2bb5a92..add4613 100644 --- a/src_files.yml +++ b/src_files.yml @@ -18,6 +18,8 @@ clic: files: - src/clicint_reg_pkg.sv - src/clicint_reg_top.sv + - src/clicintv_reg_pkg.sv + - src/clicintv_reg_top.sv - src/mclic_reg_pkg.sv - src/mclic_reg_top.sv - src/clic_reg_adapter.sv From ef906fef3356e80d333df8371c1f85312d6dbbe4 Mon Sep 17 00:00:00 2001 From: Enrico Zelioli Date: Thu, 10 Aug 2023 13:37:47 +0200 Subject: [PATCH 7/9] [ADD] VS prioritization --- Bender.yml | 2 + src/clic.sv | 206 +++++++++++++++++++++------- src/clic_reg_adapter.sv | 27 +++- src/clic_target.sv | 76 ++++++---- src/clicvs_reg_pkg.sv | 50 +++++++ src/clicvs_reg_top.sv | 297 ++++++++++++++++++++++++++++++++++++++++ src/gen/Makefile | 17 ++- src/gen/clicvs.hjson | 35 +++++ src_files.yml | 2 + 9 files changed, 628 insertions(+), 84 deletions(-) create mode 100644 src/clicvs_reg_pkg.sv create mode 100644 src/clicvs_reg_top.sv create mode 100644 src/gen/clicvs.hjson diff --git a/Bender.yml b/Bender.yml index 04e624e..18ca59c 100644 --- a/Bender.yml +++ b/Bender.yml @@ -27,6 +27,8 @@ sources: - 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 e27ba2d..8bbf489 100644 --- a/src/clic.sv +++ b/src/clic.sv @@ -17,20 +17,25 @@ `include "common_cells/assertions.svh" -module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_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, // vCLIC enable + 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) + // 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) + // do not edit below, these are derived localparam int SRC_W = $clog2(N_SOURCE), - localparam int MAX_VSCTXTS = 64 + 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, @@ -43,32 +48,55 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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 [5:0] irq_vsid_o, // up to 64 VS contexts - output logic irq_v_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 (N_VSCTXTS > MAX_VSCTXTS) - $fatal(1, "vsclic extension supports up to 64 virtual contexts"); - - // CLIC internal address size - localparam int unsigned ADDR_W = $clog2(MAX_VSCTXTS * 8 * 1024); // 19 + 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; - // Each privilege mode address space is aligned to a 8KiB physical memory region + /////////////////////////////////////////////////// + // 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; @@ -79,11 +107,18 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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) + // Reduce area by setting wire width to 0 + // if VSPRIO extension is not enabled. + localparam VsprioWidth = 8; // VSPRIO ? 8 : 1; + mclic_reg2hw_t mclic_reg2hw; clicint_reg2hw_t [N_SOURCE-1:0] clicint_reg2hw; @@ -92,14 +127,19 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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 [5:0] vsid [N_SOURCE]; // Per-IRQ Virtual Supervisor (VS) ID - logic intv [N_SOURCE]; // Per-IRQ virtualization bit + 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]; logic [N_SOURCE-1:0] le; // 0: level-sensitive 1: edge-sensitive logic [N_SOURCE-1:0] ip; @@ -127,9 +167,12 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ // 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, @@ -137,10 +180,14 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ .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), @@ -149,6 +196,9 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ .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 @@ -221,6 +271,14 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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 @@ -250,32 +308,70 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ .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 + + 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_reg2hw = '0; // assign clicintv_hw2reg = '0; - assign v_addr = '0; - assign reg_all_v_rsp = '0; + assign reg_v_req = '0; + assign reg_v_rsp = '0; + assign v_addr = '0; + assign reg_all_v_rsp = '0; end - // configuration registers - // 0x8000 (supervisor mode) - - // interrupt control and status register - // 0x9000 - 0xcfff (supervisor mode) - // mirror - // 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_all_v_req = '0; - reg_rsp_o = '0; + reg_all_v_req = '0; + reg_all_vs_req = '0; + reg_rsp_o = '0; - addr_tmp = '0; + addr_tmp = '0; unique case(reg_req_i.addr[ADDR_W-1:0]) inside MCLICCFG_START: begin @@ -343,6 +439,18 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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; @@ -389,8 +497,11 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ // 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, @@ -403,11 +514,15 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ .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), @@ -426,13 +541,6 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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]; - - // Extract Virtual Supervisor ID for the highest level, highest priority pending interrupt - assign irq_vsid_o = (VSCLIC && (irq_priv_o == S_MODE)) ? vsid[irq_id_o] : '0; - assign irq_v_o = (VSCLIC && (irq_priv_o == S_MODE)) ? intv[irq_id_o] : '0; - logic [7:0] irq_level_tmp; always_comb begin diff --git a/src/clic_reg_adapter.sv b/src/clic_reg_adapter.sv index ae0f803..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::*; import clicintv_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, @@ -29,10 +32,14 @@ module clic_reg_adapter import mclic_reg_pkg::*; import clicint_reg_pkg::*; impo 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, - output logic [7:0] intctl_o [N_SOURCE], - output logic [1:0] intmode_o [N_SOURCE], - output logic [5:0] vsid_o [N_SOURCE], // interrupt VS id - output logic intv_o [N_SOURCE], // interrupt virtualization + 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, @@ -65,4 +72,12 @@ module clic_reg_adapter import mclic_reg_pkg::*; import clicint_reg_pkg::*; impo 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 d0a303f..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,10 +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, @@ -54,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 @@ -62,6 +72,12 @@ 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; @@ -69,10 +85,9 @@ module clic_target #( // 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 // @@ -100,17 +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]; // 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 mode_tree[Pa] = ((mode_i[offset] == S_MODE) && ~intv_i[offset]) ? 2'b10 : mode_i[offset]; + 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 @@ -132,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 @@ -151,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 @@ -173,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; @@ -187,8 +206,8 @@ module clic_target #( if (irq_root_valid) begin irq_id_d = irq_root_id; irq_max_d = max_tree[0]; - // NOTE: If the interrupt priority was modified (see note above), restore nominal privilege - irq_mode_d = (mode_tree[0] == 2'b10) ? S_MODE : 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 @@ -197,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 @@ -235,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 @@ -251,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/clicvs_reg_pkg.sv b/src/clicvs_reg_pkg.sv new file mode 100644 index 0000000..5ac8fb4 --- /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 [7:0] q; + } prio0; + struct packed { + logic [7:0] q; + } prio1; + struct packed { + logic [7:0] q; + } prio2; + struct packed { + logic [7:0] q; + } prio3; + } clicvs_reg2hw_vsprio_reg_t; + + // Register -> HW type + typedef struct packed { + clicvs_reg2hw_vsprio_reg_t vsprio; // [31: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..63bc26e --- /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 [7:0] vsprio_prio0_qs; + logic [7:0] vsprio_prio0_wd; + logic vsprio_prio0_we; + logic [7:0] vsprio_prio1_qs; + logic [7:0] vsprio_prio1_wd; + logic vsprio_prio1_we; + logic [7:0] vsprio_prio2_qs; + logic [7:0] vsprio_prio2_wd; + logic vsprio_prio2_we; + logic [7:0] vsprio_prio3_qs; + logic [7:0] vsprio_prio3_wd; + logic vsprio_prio3_we; + + // Register instances + // R[vsprio]: V(False) + + // F[prio0]: 7:0 + prim_subreg #( + .DW (8), + .SWACCESS("RW"), + .RESVAL (8'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]: 15:8 + prim_subreg #( + .DW (8), + .SWACCESS("RW"), + .RESVAL (8'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]: 23:16 + prim_subreg #( + .DW (8), + .SWACCESS("RW"), + .RESVAL (8'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]: 31:24 + prim_subreg #( + .DW (8), + .SWACCESS("RW"), + .RESVAL (8'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[7:0]; + + assign vsprio_prio1_we = addr_hit[0] & reg_we & !reg_error; + assign vsprio_prio1_wd = reg_wdata[15:8]; + + assign vsprio_prio2_we = addr_hit[0] & reg_we & !reg_error; + assign vsprio_prio2_wd = reg_wdata[23:16]; + + assign vsprio_prio3_we = addr_hit[0] & reg_we & !reg_error; + assign vsprio_prio3_wd = reg_wdata[31:24]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[7:0] = vsprio_prio0_qs; + reg_rdata_next[15:8] = vsprio_prio1_qs; + reg_rdata_next[23:16] = vsprio_prio2_qs; + reg_rdata_next[31: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 47d0da4..a12ee74 100644 --- a/src/gen/Makefile +++ b/src/gen/Makefile @@ -19,9 +19,15 @@ 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: clicintv_reg_pkg.sv clicintv_reg_top.sv 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 . @@ -32,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 clicintv.h +headers: $(HDRS) clic.h: mclic.hjson $(REGTOOL) --cdefines mclic.hjson > $@ @@ -43,11 +49,14 @@ clicint.h: 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 clicintv_reg_pkg.sv clicintv_reg_top.sv .. + cp $(SRCS) .. .PHONY: clean clean: - rm clic.h clicint.h clicintv.h mclic_reg_pkg.sv mclic_reg_top.sv clicint_reg_pkg.sv clicint_reg_top.sv clicintv_reg_pkg.sv clicintv_reg_top.sv + rm $(HDRS) $(SRCS) diff --git a/src/gen/clicvs.hjson b/src/gen/clicvs.hjson new file mode 100644 index 0000000..284c3cb --- /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: "31:24", name: "prio3", desc: "VS3 priority" }, + { bits: "23:16", name: "prio2", desc: "VS2 priority" }, + { bits: "15:8", name: "prio1", desc: "VS1 priority" }, + { bits: "7:0", name: "prio0", desc: "VS0 priority" }, + ], + } + ] +} diff --git a/src_files.yml b/src_files.yml index add4613..690f9d5 100644 --- a/src_files.yml +++ b/src_files.yml @@ -20,6 +20,8 @@ clic: - 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 From 6e9fc9c809d73ff84be1b387e2df8cdf4b7dd6bb Mon Sep 17 00:00:00 2001 From: Enrico Zelioli Date: Fri, 11 Aug 2023 13:53:22 +0200 Subject: [PATCH 8/9] [FIX] Typo in number of levels configuration --- src/clic.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clic.sv b/src/clic.sv index 8bbf489..8d69207 100644 --- a/src/clic.sv +++ b/src/clic.sv @@ -537,7 +537,7 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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 From fb4e942902136cfa9c0997af935146121a9b77c1 Mon Sep 17 00:00:00 2001 From: Enrico Zelioli Date: Mon, 11 Sep 2023 10:56:23 +0200 Subject: [PATCH 9/9] [MOD] Set VSprio width to 1bit --- src/clic.sv | 10 +++----- src/clicvs_reg_pkg.sv | 10 ++++---- src/clicvs_reg_top.sv | 56 +++++++++++++++++++++---------------------- src/gen/clicvs.hjson | 8 +++---- 4 files changed, 40 insertions(+), 44 deletions(-) diff --git a/src/clic.sv b/src/clic.sv index 8d69207..a462249 100644 --- a/src/clic.sv +++ b/src/clic.sv @@ -30,7 +30,8 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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 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), @@ -115,10 +116,6 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ `define VSCLICINT_START(i) ('h08000 * (i + 1) + 'h01000) `define VSCLICINT_END(i) ('h08000 * (i + 1) + 'h04fff) - // Reduce area by setting wire width to 0 - // if VSPRIO extension is not enabled. - localparam VsprioWidth = 8; // VSPRIO ? 8 : 1; - mclic_reg2hw_t mclic_reg2hw; clicint_reg2hw_t [N_SOURCE-1:0] clicint_reg2hw; @@ -139,7 +136,7 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ 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]; + 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; @@ -463,7 +460,6 @@ module clic import mclic_reg_pkg::*; import clicint_reg_pkg::*; import clicintv_ if (VSCLIC) begin for (int i = 1; i <= N_VSCTXTS; i++) begin case(reg_req_i.addr[ADDR_W-1:0]) inside - // TODO: whether / how to grant access to MCLICCFG register `VSCLICCFG_START(i): begin // inaccesible (all zero) reg_rsp_o.rdata = '0; diff --git a/src/clicvs_reg_pkg.sv b/src/clicvs_reg_pkg.sv index 5ac8fb4..ea3f6e7 100644 --- a/src/clicvs_reg_pkg.sv +++ b/src/clicvs_reg_pkg.sv @@ -15,22 +15,22 @@ package clicvs_reg_pkg; typedef struct packed { struct packed { - logic [7:0] q; + logic q; } prio0; struct packed { - logic [7:0] q; + logic q; } prio1; struct packed { - logic [7:0] q; + logic q; } prio2; struct packed { - logic [7:0] q; + logic q; } prio3; } clicvs_reg2hw_vsprio_reg_t; // Register -> HW type typedef struct packed { - clicvs_reg2hw_vsprio_reg_t vsprio; // [31:0] + clicvs_reg2hw_vsprio_reg_t vsprio; // [3:0] } clicvs_reg2hw_t; // Register offsets diff --git a/src/clicvs_reg_top.sv b/src/clicvs_reg_top.sv index 63bc26e..f836899 100644 --- a/src/clicvs_reg_top.sv +++ b/src/clicvs_reg_top.sv @@ -67,27 +67,27 @@ module clicvs_reg_top #( // Define SW related signals // Format: __{wd|we|qs} // or _{wd|we|qs} if field == 1 or 0 - logic [7:0] vsprio_prio0_qs; - logic [7:0] vsprio_prio0_wd; + logic vsprio_prio0_qs; + logic vsprio_prio0_wd; logic vsprio_prio0_we; - logic [7:0] vsprio_prio1_qs; - logic [7:0] vsprio_prio1_wd; + logic vsprio_prio1_qs; + logic vsprio_prio1_wd; logic vsprio_prio1_we; - logic [7:0] vsprio_prio2_qs; - logic [7:0] vsprio_prio2_wd; + logic vsprio_prio2_qs; + logic vsprio_prio2_wd; logic vsprio_prio2_we; - logic [7:0] vsprio_prio3_qs; - logic [7:0] vsprio_prio3_wd; + logic vsprio_prio3_qs; + logic vsprio_prio3_wd; logic vsprio_prio3_we; // Register instances // R[vsprio]: V(False) - // F[prio0]: 7:0 + // F[prio0]: 0:0 prim_subreg #( - .DW (8), + .DW (1), .SWACCESS("RW"), - .RESVAL (8'h0) + .RESVAL (1'h0) ) u_vsprio_prio0 ( .clk_i (clk_i ), .rst_ni (rst_ni ), @@ -109,11 +109,11 @@ module clicvs_reg_top #( ); - // F[prio1]: 15:8 + // F[prio1]: 8:8 prim_subreg #( - .DW (8), + .DW (1), .SWACCESS("RW"), - .RESVAL (8'h0) + .RESVAL (1'h0) ) u_vsprio_prio1 ( .clk_i (clk_i ), .rst_ni (rst_ni ), @@ -135,11 +135,11 @@ module clicvs_reg_top #( ); - // F[prio2]: 23:16 + // F[prio2]: 16:16 prim_subreg #( - .DW (8), + .DW (1), .SWACCESS("RW"), - .RESVAL (8'h0) + .RESVAL (1'h0) ) u_vsprio_prio2 ( .clk_i (clk_i ), .rst_ni (rst_ni ), @@ -161,11 +161,11 @@ module clicvs_reg_top #( ); - // F[prio3]: 31:24 + // F[prio3]: 24:24 prim_subreg #( - .DW (8), + .DW (1), .SWACCESS("RW"), - .RESVAL (8'h0) + .RESVAL (1'h0) ) u_vsprio_prio3 ( .clk_i (clk_i ), .rst_ni (rst_ni ), @@ -204,26 +204,26 @@ module clicvs_reg_top #( end assign vsprio_prio0_we = addr_hit[0] & reg_we & !reg_error; - assign vsprio_prio0_wd = reg_wdata[7:0]; + assign vsprio_prio0_wd = reg_wdata[0]; assign vsprio_prio1_we = addr_hit[0] & reg_we & !reg_error; - assign vsprio_prio1_wd = reg_wdata[15:8]; + assign vsprio_prio1_wd = reg_wdata[8]; assign vsprio_prio2_we = addr_hit[0] & reg_we & !reg_error; - assign vsprio_prio2_wd = reg_wdata[23:16]; + assign vsprio_prio2_wd = reg_wdata[16]; assign vsprio_prio3_we = addr_hit[0] & reg_we & !reg_error; - assign vsprio_prio3_wd = reg_wdata[31:24]; + 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[7:0] = vsprio_prio0_qs; - reg_rdata_next[15:8] = vsprio_prio1_qs; - reg_rdata_next[23:16] = vsprio_prio2_qs; - reg_rdata_next[31:24] = vsprio_prio3_qs; + 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 diff --git a/src/gen/clicvs.hjson b/src/gen/clicvs.hjson index 284c3cb..fccc122 100644 --- a/src/gen/clicvs.hjson +++ b/src/gen/clicvs.hjson @@ -25,10 +25,10 @@ swaccess: "rw", hwaccess: "hro", fields: [ - { bits: "31:24", name: "prio3", desc: "VS3 priority" }, - { bits: "23:16", name: "prio2", desc: "VS2 priority" }, - { bits: "15:8", name: "prio1", desc: "VS1 priority" }, - { bits: "7:0", name: "prio0", desc: "VS0 priority" }, + { 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" }, ], } ]