diff --git a/dv/formal/check/top.sv b/dv/formal/check/top.sv index cc6924d19..9b1153f47 100644 --- a/dv/formal/check/top.sv +++ b/dv/formal/check/top.sv @@ -142,7 +142,33 @@ localparam logic [31:0] CSR_MIMPID_VALUE = 32'b0; default clocking @(posedge clk_i); endclocking +// The TRVK (CHERIoT revocation) filter is only instantiated when BaseIsa is +// BaseIsaRV32IorCHERIoT. The core has no CHERIoT support yet and the formal spec +// is the plain RISC-V Sail model, so select BaseIsaRV32I to bypass the filter +// (a combinational pass-through of the data interface). The filter's ports still +// exist on ibex_top, so provide nets for the `.*` connection below: drive the +// inputs to their inactive values and leave the outputs as sinks. +logic [31:0] trvk_heap_base_addr_i; +logic data_tag_o; +logic data_tag_i; +logic trvk_revbm_req_o; +logic trvk_revbm_gnt_i; +logic trvk_revbm_rvalid_i; +logic [31:0] trvk_revbm_addr_o; +logic [31:0] trvk_revbm_rdata_i; +logic [ 6:0] trvk_revbm_rdata_intg_i; +logic trvk_revbm_err_i; + +assign trvk_heap_base_addr_i = 32'b0; +assign data_tag_i = 1'b0; +assign trvk_revbm_gnt_i = 1'b0; +assign trvk_revbm_rvalid_i = 1'b0; +assign trvk_revbm_rdata_i = 32'b0; +assign trvk_revbm_rdata_intg_i = 7'b0; +assign trvk_revbm_err_i = 1'b0; + ibex_top #( + .BaseIsa(ibex_pkg::BaseIsaRV32I), .DmHaltAddr(DmHaltAddr), .DmExceptionAddr(DmExceptionAddr), .SecureIbex(SecureIbex), diff --git a/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv b/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv index e93523505..755c5a66c 100644 --- a/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv +++ b/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv @@ -180,6 +180,8 @@ module ibex_riscv_compliance ( // First instruction executed is at 0x0 + 0x80 .boot_addr_i (32'h00000000 ), + .trvk_heap_base_addr_i (32'h00000000 ), + .instr_req_o (host_req[CoreI] ), .instr_gnt_i (host_gnt[CoreI] ), .instr_rvalid_i (host_rvalid[CoreI] ), @@ -196,10 +198,20 @@ module ibex_riscv_compliance ( .data_addr_o (host_addr[CoreD] ), .data_wdata_o (host_wdata[CoreD] ), .data_wdata_intg_o ( ), + .data_tag_o ( ), .data_rdata_i (host_rdata[CoreD] ), .data_rdata_intg_i (ibex_data_rdata_intg ), + .data_tag_i (1'b0 ), .data_err_i (host_err[CoreD] ), + .trvk_revbm_req_o ( ), + .trvk_revbm_gnt_i (1'b0 ), + .trvk_revbm_rvalid_i (1'b0 ), + .trvk_revbm_addr_o ( ), + .trvk_revbm_rdata_i ('0 ), + .trvk_revbm_rdata_intg_i ('0 ), + .trvk_revbm_err_i (1'b0 ), + .irq_software_i (1'b0 ), .irq_timer_i (1'b0 ), .irq_external_i (1'b0 ), diff --git a/dv/uvm/core_ibex/ibex_dv.f b/dv/uvm/core_ibex/ibex_dv.f index 877870069..a5abbfd71 100644 --- a/dv/uvm/core_ibex/ibex_dv.f +++ b/dv/uvm/core_ibex/ibex_dv.f @@ -50,10 +50,17 @@ ${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_39_32_dec.sv ${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_72_64_enc.sv ${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_72_64_dec.sv +${LOWRISC_IP_DIR}/ip/prim/rtl/prim_fifo_sync_cnt.sv +${LOWRISC_IP_DIR}/ip/prim/rtl/prim_fifo_sync.sv + +// Vendored PULP common cells (used by the TRVK filter). +${PRJ_DIR}/vendor/pulp_common_cells/rtl/stream_fork.sv +${PRJ_DIR}/vendor/pulp_common_cells/rtl/stream_join_dynamic.sv // ibex CORE RTL files +incdir+${PRJ_DIR}/rtl ${PRJ_DIR}/rtl/ibex_pkg.sv +${PRJ_DIR}/rtl/ibex_cheriot_pkg.sv ${PRJ_DIR}/rtl/ibex_tracer_pkg.sv ${PRJ_DIR}/rtl/ibex_tracer.sv ${PRJ_DIR}/rtl/ibex_alu.sv @@ -81,6 +88,7 @@ ${PRJ_DIR}/rtl/ibex_register_file_latch.sv ${PRJ_DIR}/rtl/ibex_pmp.sv ${PRJ_DIR}/rtl/ibex_core.sv +${PRJ_DIR}/rtl/ibex_trvk.sv ${PRJ_DIR}/rtl/ibex_top.sv ${PRJ_DIR}/rtl/ibex_top_tracing.sv diff --git a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv index 0c52939a0..4dd9d0f47 100644 --- a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv +++ b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv @@ -95,29 +95,30 @@ module core_ibex_tb_top; assign {scramble_key, scramble_nonce} = scrambling_key_if.d_data; ibex_top_tracing #( - .PMPEnable (PMPEnable ), - .PMPGranularity (PMPGranularity ), - .PMPNumRegions (PMPNumRegions ), - .MHPMCounterNum (MHPMCounterNum ), - .MHPMCounterWidth (MHPMCounterWidth ), - .RV32E (RV32E ), - .RV32M (RV32M ), - .RV32B (RV32B ), - .RegFile (RegFile ), - .BranchTargetALU (BranchTargetALU ), - .WritebackStage (WritebackStage ), - .ICache (ICache ), - .ICacheECC (ICacheECC ), - .ICacheTweakInfection (ICacheTweakInfection), - .SecureIbex (SecureIbex ), - .LockstepOffset (LockstepOffset ), - .ICacheScramble (ICacheScramble ), - .BranchPredictor (BranchPredictor ), - .DbgTriggerEn (DbgTriggerEn ), - .DmBaseAddr (DmBaseAddr ), - .DmAddrMask (DmAddrMask ), - .DmHaltAddr (DmHaltAddr ), - .DmExceptionAddr (DmExceptionAddr ) + .BaseIsa (ibex_pkg::BaseIsaRV32I), + .PMPEnable (PMPEnable ), + .PMPGranularity (PMPGranularity ), + .PMPNumRegions (PMPNumRegions ), + .MHPMCounterNum (MHPMCounterNum ), + .MHPMCounterWidth (MHPMCounterWidth ), + .RV32E (RV32E ), + .RV32M (RV32M ), + .RV32B (RV32B ), + .RegFile (RegFile ), + .BranchTargetALU (BranchTargetALU ), + .WritebackStage (WritebackStage ), + .ICache (ICache ), + .ICacheECC (ICacheECC ), + .ICacheTweakInfection (ICacheTweakInfection ), + .SecureIbex (SecureIbex ), + .LockstepOffset (LockstepOffset ), + .ICacheScramble (ICacheScramble ), + .BranchPredictor (BranchPredictor ), + .DbgTriggerEn (DbgTriggerEn ), + .DmBaseAddr (DmBaseAddr ), + .DmAddrMask (DmAddrMask ), + .DmHaltAddr (DmHaltAddr ), + .DmExceptionAddr (DmExceptionAddr ) ) dut ( .clk_i (clk ), @@ -132,6 +133,7 @@ module core_ibex_tb_top; .hart_id_i (32'b0 ), .boot_addr_i (BootAddr ), + .trvk_heap_base_addr_i (32'b0 ), .instr_req_o (instr_mem_vif.request ), .instr_gnt_i (instr_mem_vif.grant ), @@ -149,10 +151,20 @@ module core_ibex_tb_top; .data_be_o (data_mem_vif.be ), .data_rdata_i (data_mem_vif.rdata ), .data_rdata_intg_i (data_mem_vif.rintg ), + .data_tag_i (1'b0 ), .data_wdata_o (data_mem_vif.wdata ), .data_wdata_intg_o (data_mem_vif.wintg ), + .data_tag_o ( ), .data_err_i (data_mem_vif.error ), + .trvk_revbm_req_o ( ), + .trvk_revbm_gnt_i (1'b0 ), + .trvk_revbm_rvalid_i (1'b0 ), + .trvk_revbm_addr_o ( ), + .trvk_revbm_rdata_i ('b0 ), + .trvk_revbm_rdata_intg_i ('b0 ), + .trvk_revbm_err_i (1'b0 ), + .irq_software_i (irq_vif.irq_software ), .irq_timer_i (irq_vif.irq_timer ), .irq_external_i (irq_vif.irq_external ), diff --git a/examples/simple_system/rtl/ibex_simple_system.sv b/examples/simple_system/rtl/ibex_simple_system.sv index 01dc036dc..83e6a8fb5 100644 --- a/examples/simple_system/rtl/ibex_simple_system.sv +++ b/examples/simple_system/rtl/ibex_simple_system.sv @@ -240,6 +240,8 @@ module ibex_simple_system ( // First instruction executed is at 0x0 + 0x80 .boot_addr_i (32'h00100000), + .trvk_heap_base_addr_i (32'h00000000), + .instr_req_o (instr_req), .instr_gnt_i (instr_gnt), .instr_rvalid_i (instr_rvalid), @@ -256,10 +258,20 @@ module ibex_simple_system ( .data_addr_o (host_addr[CoreD]), .data_wdata_o (host_wdata[CoreD]), .data_wdata_intg_o (), + .data_tag_o (), .data_rdata_i (host_rdata[CoreD]), .data_rdata_intg_i (data_rdata_intg), + .data_tag_i (1'b0), .data_err_i (host_err[CoreD]), + .trvk_revbm_req_o (), + .trvk_revbm_gnt_i (1'b0), + .trvk_revbm_rvalid_i (1'b0), + .trvk_revbm_addr_o (), + .trvk_revbm_rdata_i ('0), + .trvk_revbm_rdata_intg_i ('0), + .trvk_revbm_err_i (1'b0), + .irq_software_i (1'b0), .irq_timer_i (timer_irq), .irq_external_i (1'b0), diff --git a/ibex_core.core b/ibex_core.core index ab0ee64e8..84dd40d49 100644 --- a/ibex_core.core +++ b/ibex_core.core @@ -12,6 +12,7 @@ filesets: - lowrisc:prim:clock_gating - lowrisc:prim:lfsr - lowrisc:prim:mubi + - lowrisc:prim:fifo - lowrisc:ibex:ibex_pkg - lowrisc:ibex:ibex_icache - lowrisc:dv:dv_fcov_macros diff --git a/ibex_pkg.core b/ibex_pkg.core index 17f47e08c..68a512543 100644 --- a/ibex_pkg.core +++ b/ibex_pkg.core @@ -9,6 +9,7 @@ filesets: files_rtl: files: - rtl/ibex_pkg.sv + - rtl/ibex_cheriot_pkg.sv file_type: systemVerilogSource targets: diff --git a/ibex_top.core b/ibex_top.core index 82edbe133..0bb94c2e3 100644 --- a/ibex_top.core +++ b/ibex_top.core @@ -19,10 +19,12 @@ filesets: - lowrisc:prim:onehot_check - lowrisc:prim:onehot - lowrisc:prim:util + - pulp-platform:common_cells:common_cells files: - rtl/ibex_register_file_ff.sv # generic FF-based - rtl/ibex_register_file_fpga.sv # FPGA - rtl/ibex_register_file_latch.sv # ASIC + - rtl/ibex_trvk.sv - rtl/ibex_lockstep.sv - rtl/ibex_top.sv file_type: systemVerilogSource diff --git a/rtl/ibex_cheriot_pkg.sv b/rtl/ibex_cheriot_pkg.sv new file mode 100644 index 000000000..d944edf43 --- /dev/null +++ b/rtl/ibex_cheriot_pkg.sv @@ -0,0 +1,56 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package ibex_cheriot_pkg; + + parameter int unsigned TOP_W = 9; + parameter int unsigned BOT_W = 9; + parameter int unsigned EXP_W = 5; + parameter int unsigned OTYPE_W = 3; + parameter int unsigned CPERMS_W = 6; + + + // Obtain 32-bit representation of top + function automatic logic[32:0] get_bound33(logic [TOP_W-1:0] top, logic [1:0] cor, + logic [EXP_W-1:0] exponent, logic [31:0] addr); + logic [32:0] t1, t2, mask, cor_val; + + if (cor[1]) + // negative sign extension + cor_val = {33{cor[1]}}; + else + cor_val = {32'h0, (~cor[1]) & cor[0]}; + + cor_val = (cor_val << exponent) << TOP_W; + mask = (33'h1_ffff_ffff << exponent) << TOP_W; + + // apply correction and truncate + t1 = ({1'b0, addr} & mask) + cor_val; + // extend to 32 bit + t2 = {24'h0, top}; + t1 = t1 | (t2 << exponent); + + return t1; + endfunction + + + // Update the top/base correction for a cap + function automatic logic [2:0] update_temp_fields(logic [TOP_W-1:0] top, logic [BOT_W-1:0] base, + logic [BOT_W-1:0] addrmi); + logic top_hi, addr_hi; + logic [2:0] res3; + + top_hi = (top < base); + addr_hi = (addrmi < base); + + // top_cor + res3[2:1] = (top_hi == addr_hi)? 2'b00 : ((top_hi && (!addr_hi))? 2'b01 : 2'b11); + + // base_cor + res3[0] = (addr_hi) ? 1'b1 : 1'b0; + + return res3; + endfunction + +endpackage diff --git a/rtl/ibex_pkg.sv b/rtl/ibex_pkg.sv index 216aa1ab3..e3185e8b8 100644 --- a/rtl/ibex_pkg.sv +++ b/rtl/ibex_pkg.sv @@ -32,6 +32,11 @@ package ibex_pkg; // Parameter Enums // ///////////////////// + typedef enum integer { + BaseIsaRV32I = 0, // only RV32I + BaseIsaRV32IorCHERIoT = 1 // dual base ISA: RV32I/CHERIoT runtime switchable + } base_isa_e; + typedef enum integer { RegFileFF = 0, RegFileFPGA = 1, diff --git a/rtl/ibex_top.sv b/rtl/ibex_top.sv index af4813c79..2b8e505e3 100644 --- a/rtl/ibex_top.sv +++ b/rtl/ibex_top.sv @@ -13,6 +13,7 @@ * Top level module of the ibex RISC-V core */ module ibex_top import ibex_pkg::*; #( + parameter ibex_pkg::base_isa_e BaseIsa = ibex_pkg::BaseIsaRV32IorCHERIoT, parameter bit PMPEnable = 1'b0, parameter int unsigned PMPGranularity = 0, parameter int unsigned PMPNumRegions = 4, @@ -21,6 +22,8 @@ module ibex_top import ibex_pkg::*; #( parameter ibex_pkg::pmp_cfg_t PMPRstCfg[PMP_MAX_REGIONS] = ibex_pkg::PmpCfgRst, parameter logic [PMP_ADDR_MSB:0] PMPRstAddr[PMP_MAX_REGIONS] = ibex_pkg::PmpAddrRst, parameter ibex_pkg::pmp_mseccfg_t PMPRstMsecCfg = ibex_pkg::PmpMseccfgRst, + parameter int unsigned CheriotRevBitmapAddrWidth = 32'd9, + parameter int unsigned CheriotRevBitmapBaseAddr = 32'h0, parameter bit RV32E = 1'b0, parameter rv32m_e RV32M = RV32MFast, parameter rv32b_e RV32B = RV32BNone, @@ -73,6 +76,8 @@ module ibex_top import ibex_pkg::*; #( input logic [31:0] hart_id_i, input logic [31:0] boot_addr_i, + input logic [31:0] trvk_heap_base_addr_i, + // Instruction memory interface output logic instr_req_o, input logic instr_gnt_i, @@ -91,10 +96,21 @@ module ibex_top import ibex_pkg::*; #( output logic [31:0] data_addr_o, output logic [31:0] data_wdata_o, output logic [6:0] data_wdata_intg_o, + output logic data_tag_o, input logic [31:0] data_rdata_i, input logic [6:0] data_rdata_intg_i, + input logic data_tag_i, input logic data_err_i, + // TRVK revocation bitmap read interface + output logic trvk_revbm_req_o, + input logic trvk_revbm_gnt_i, + input logic trvk_revbm_rvalid_i, + output logic [31:0] trvk_revbm_addr_o, + input logic [31:0] trvk_revbm_rdata_i, + input logic [6:0] trvk_revbm_rdata_intg_i, + input logic trvk_revbm_err_i, + // Interrupt inputs input logic irq_software_i, input logic irq_timer_i, @@ -222,6 +238,23 @@ module ibex_top import ibex_pkg::*; #( logic [MemDataWidth-1:0] data_rdata_core; logic [MemDataWidth-1:0] instr_rdata_core; + // Core <-> TRVK connection + logic trvk_req; + logic trvk_gnt; + logic trvk_rvalid; + logic trvk_we; + logic [3:0] trvk_be; + logic [31:0] trvk_addr; + logic [31:0] trvk_wdata; + logic [6:0] trvk_wdata_intg; + logic trvk_wtag; + logic [31:0] trvk_rdata; + logic [6:0] trvk_rdata_intg; + logic trvk_rtag; + logic trvk_err; + logic trvk_revbm_data_intg_error; + logic trvk_revbm_device_error; + // Core <-> RAMs signals logic [IC_NUM_WAYS-1:0] ic_tag_req; logic ic_tag_write; @@ -305,16 +338,16 @@ module ibex_top import ibex_pkg::*; #( // ibex_core takes integrity and data bits together. Combine the separate integrity and data // inputs here. - assign data_rdata_core[31:0] = data_rdata_i; + assign data_rdata_core[31:0] = trvk_rdata; assign instr_rdata_core[31:0] = instr_rdata_i; if (MemECC) begin : gen_mem_rdata_ecc - assign data_rdata_core[38:32] = data_rdata_intg_i; + assign data_rdata_core[38:32] = trvk_rdata_intg; assign instr_rdata_core[38:32] = instr_rdata_intg_i; end else begin : gen_non_mem_rdata_ecc logic unused_intg; - assign unused_intg = ^{instr_rdata_intg_i, data_rdata_intg_i}; + assign unused_intg = ^{instr_rdata_intg_i, trvk_rdata_intg}; end ibex_core #( @@ -370,15 +403,15 @@ module ibex_top import ibex_pkg::*; #( .instr_rdata_i(instr_rdata_core), .instr_err_i, - .data_req_o, - .data_gnt_i, - .data_rvalid_i, - .data_we_o, - .data_be_o, - .data_addr_o, - .data_wdata_o(data_wdata_core), - .data_rdata_i(data_rdata_core), - .data_err_i, + .data_req_o (trvk_req), + .data_gnt_i (trvk_gnt), + .data_rvalid_i(trvk_rvalid), + .data_we_o (trvk_we), + .data_be_o (trvk_be), + .data_addr_o (trvk_addr), + .data_wdata_o (data_wdata_core), + .data_rdata_i (data_rdata_core), + .data_err_i (trvk_err), .dummy_instr_id_o (dummy_instr_id), .dummy_instr_wb_o (dummy_instr_wb), @@ -777,15 +810,15 @@ module ibex_top import ibex_pkg::*; #( assign icache_data_alert = '{default:'b0}; end - assign data_wdata_o = data_wdata_core[31:0]; + assign trvk_wdata = data_wdata_core[31:0]; if (MemECC) begin : gen_mem_wdata_ecc prim_buf #(.Width(7)) u_prim_buf_data_wdata_intg ( .in_i (data_wdata_core[38:32]), - .out_o(data_wdata_intg_o) + .out_o(trvk_wdata_intg) ); end else begin : gen_no_mem_ecc - assign data_wdata_intg_o = '0; + assign trvk_wdata_intg = '0; end // Redundant lockstep core implementation @@ -806,15 +839,15 @@ module ibex_top import ibex_pkg::*; #( instr_addr_o, instr_rdata_core, instr_err_i, - data_req_o, - data_gnt_i, - data_rvalid_i, - data_we_o, - data_be_o, - data_addr_o, - data_wdata_o, + trvk_req, + trvk_gnt, + trvk_rvalid, + trvk_we, + trvk_be, + trvk_addr, + trvk_wdata, data_rdata_core, - data_err_i, + trvk_err, rf_rdata_a, rf_rdata_b, ic_tag_req, @@ -901,15 +934,15 @@ module ibex_top import ibex_pkg::*; #( instr_addr_o, instr_rdata_core, instr_err_i, - data_req_o, - data_gnt_i, - data_rvalid_i, - data_we_o, - data_be_o, - data_addr_o, - data_wdata_o, + trvk_req, + trvk_gnt, + trvk_rvalid, + trvk_we, + trvk_be, + trvk_addr, + trvk_wdata, data_rdata_core, - data_err_i, + trvk_err, rf_rdata_a, rf_rdata_b, ic_tag_req, @@ -1148,15 +1181,112 @@ module ibex_top import ibex_pkg::*; #( assign unused_scan = scan_rst_ni; end + + ////////// + // TRVK // + ////////// + + if (BaseIsa == BaseIsaRV32IorCHERIoT) begin : gen_cheriot_trvk + ibex_trvk #( + .NumOutstanding(32'd2), + .RevBitmapAddrWidth(CheriotRevBitmapAddrWidth), + .RevBitmapBaseAddr(CheriotRevBitmapBaseAddr) + ) i_ibex_trvk ( + .clk_i (clk), + .rst_ni (rst_ni), + .heap_base_addr_i (trvk_heap_base_addr_i), + .upstream_req_i (trvk_req), + .upstream_gnt_o (trvk_gnt), + .upstream_rvalid_o (trvk_rvalid), + .upstream_we_i (trvk_we), + .upstream_be_i (trvk_be), + .upstream_addr_i (trvk_addr), + .upstream_wdata_i (trvk_wdata), + .upstream_wdata_intg_i (trvk_wdata_intg), + .upstream_rdata_o (trvk_rdata), + .upstream_rdata_intg_o (trvk_rdata_intg), + .upstream_err_o (trvk_err), + .upstream_tag_i (trvk_wtag), + .upstream_tag_o (trvk_rtag), + .downstream_req_o (data_req_o), + .downstream_gnt_i (data_gnt_i), + .downstream_rvalid_i (data_rvalid_i), + .downstream_we_o (data_we_o), + .downstream_be_o (data_be_o), + .downstream_addr_o (data_addr_o), + .downstream_wdata_o (data_wdata_o), + .downstream_wdata_intg_o(data_wdata_intg_o), + .downstream_rdata_i (data_rdata_i), + .downstream_rdata_intg_i(data_rdata_intg_i), + .downstream_err_i (data_err_i), + .downstream_tag_o (data_tag_o), + .downstream_tag_i (data_tag_i), + .revbm_req_o (trvk_revbm_req_o), + .revbm_gnt_i (trvk_revbm_gnt_i), + .revbm_rvalid_i (trvk_revbm_rvalid_i), + .revbm_addr_o (trvk_revbm_addr_o), + .revbm_rdata_i (trvk_revbm_rdata_i), + .revbm_rdata_intg_i (trvk_revbm_rdata_intg_i), + .revbm_err_i (trvk_revbm_err_i), + .revbm_data_intg_error_o(trvk_revbm_data_intg_error), + .revbm_device_error_o (trvk_revbm_device_error) + ); + + // Tag connection towards the core is tied-off for now. This will change as soon as the core + // is updated to feature CHERIoT support. + logic unused_trvk; + assign trvk_wtag = 1'b0; + assign unused_trvk = trvk_rtag; + + end else begin : gen_no_cheriot_trvk + + logic unused_trvk; + + assign trvk_revbm_req_o = '0; + assign trvk_revbm_addr_o = '0; + assign trvk_wtag = 1'b0; + assign trvk_rtag = 1'b0; + assign data_tag_o = 1'b0; + assign trvk_revbm_data_intg_error = 1'b0; + assign trvk_revbm_device_error = 1'b0; + assign unused_trvk = ^{ + trvk_heap_base_addr_i, + trvk_revbm_gnt_i, + trvk_revbm_rvalid_i, + trvk_revbm_rdata_i, + trvk_revbm_rdata_intg_i, + trvk_revbm_err_i, + trvk_rtag, + data_tag_i + }; + + // Through-connect TRVK + assign data_req_o = trvk_req; + assign trvk_gnt = data_gnt_i; + assign trvk_rvalid = data_rvalid_i; + assign data_we_o = trvk_we; + assign data_be_o = trvk_be; + assign data_addr_o = trvk_addr; + assign data_wdata_o = trvk_wdata; + assign data_wdata_intg_o = trvk_wdata_intg; + assign trvk_rdata = data_rdata_i; + assign trvk_rdata_intg = data_rdata_intg_i; + assign trvk_err = data_err_i; + end + + // Enable or disable iCache multi bit encoding checking error generation. // If enabled and a MuBi encoding error is detected, raise a major alert. logic icache_alert_major_internal; assign icache_alert_major_internal = (|icache_tag_alert) | (|icache_data_alert); - assign alert_major_internal_o = core_alert_major_internal | + assign alert_major_internal_o = core_alert_major_internal | lockstep_alert_major_internal | icache_alert_major_internal; - assign alert_major_bus_o = core_alert_major_bus | lockstep_alert_major_bus; + assign alert_major_bus_o = core_alert_major_bus | + lockstep_alert_major_bus | + trvk_revbm_data_intg_error | + trvk_revbm_device_error; assign alert_minor_o = core_alert_minor | lockstep_alert_minor; // X checks for top-level outputs diff --git a/rtl/ibex_top_tracing.sv b/rtl/ibex_top_tracing.sv index 85c837575..01662936b 100644 --- a/rtl/ibex_top_tracing.sv +++ b/rtl/ibex_top_tracing.sv @@ -7,6 +7,7 @@ */ module ibex_top_tracing import ibex_pkg::*; #( + parameter base_isa_e BaseIsa = BaseIsaRV32IorCHERIoT, parameter bit PMPEnable = 1'b0, parameter int unsigned PMPGranularity = 0, parameter int unsigned PMPNumRegions = 4, @@ -49,6 +50,7 @@ module ibex_top_tracing import ibex_pkg::*; #( input prim_ram_1p_pkg::ram_1p_cfg_req_t [ibex_pkg::IC_NUM_WAYS-1:0] ram_cfg_icache_data_i, output prim_ram_1p_pkg::ram_1p_cfg_rsp_t [ibex_pkg::IC_NUM_WAYS-1:0] ram_cfg_icache_data_o, + input logic [31:0] trvk_heap_base_addr_i, input logic [31:0] hart_id_i, input logic [31:0] boot_addr_i, @@ -71,10 +73,21 @@ module ibex_top_tracing import ibex_pkg::*; #( output logic [31:0] data_addr_o, output logic [31:0] data_wdata_o, output logic [6:0] data_wdata_intg_o, + output logic data_tag_o, input logic [31:0] data_rdata_i, input logic [6:0] data_rdata_intg_i, + input logic data_tag_i, input logic data_err_i, + // TRVK revocation bitmap read interface + output logic trvk_revbm_req_o, + input logic trvk_revbm_gnt_i, + input logic trvk_revbm_rvalid_i, + output logic [31:0] trvk_revbm_addr_o, + input logic [31:0] trvk_revbm_rdata_i, + input logic [6:0] trvk_revbm_rdata_intg_i, + input logic trvk_revbm_err_i, + // Interrupt inputs input logic irq_software_i, input logic irq_timer_i, @@ -196,6 +209,7 @@ module ibex_top_tracing import ibex_pkg::*; #( assign unused_rvfi_ext_expanded_insn_last = rvfi_ext_expanded_insn_last; ibex_top #( + .BaseIsa ( BaseIsa ), .PMPEnable ( PMPEnable ), .PMPGranularity ( PMPGranularity ), .PMPNumRegions ( PMPNumRegions ), @@ -259,6 +273,17 @@ module ibex_top_tracing import ibex_pkg::*; #( .data_rdata_intg_i, .data_err_i, + .trvk_heap_base_addr_i, + .data_tag_o, + .data_tag_i, + .trvk_revbm_req_o, + .trvk_revbm_gnt_i, + .trvk_revbm_rvalid_i, + .trvk_revbm_addr_o, + .trvk_revbm_rdata_i, + .trvk_revbm_rdata_intg_i, + .trvk_revbm_err_i, + .irq_software_i, .irq_timer_i, .irq_external_i, diff --git a/rtl/ibex_trvk.sv b/rtl/ibex_trvk.sv new file mode 100644 index 000000000..b02835718 --- /dev/null +++ b/rtl/ibex_trvk.sv @@ -0,0 +1,383 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module ibex_trvk #( + // The number of outstanding transaction the IP supports + parameter int unsigned NumOutstanding = 32'd4, + // The width of the meta memory address space used to store the revocation bits + parameter int unsigned RevBitmapAddrWidth = 32'd9, + // The base address of the meta SRAM holding the revocation bitmap + parameter int unsigned RevBitmapBaseAddr = 32'h0000_0000 +)( + input clk_i, + input rst_ni, + + // The base address of the (heap) memory where to be revocable capabilities point to + input logic [31:0] heap_base_addr_i, + + // Upstream port + input logic upstream_req_i, + output logic upstream_gnt_o, + output logic upstream_rvalid_o, + input logic upstream_we_i, + input logic [3:0] upstream_be_i, + input logic [31:0] upstream_addr_i, + input logic [31:0] upstream_wdata_i, + input logic [6:0] upstream_wdata_intg_i, + output logic [31:0] upstream_rdata_o, + output logic [6:0] upstream_rdata_intg_o, + output logic upstream_err_o, + input logic upstream_tag_i, + output logic upstream_tag_o, + + // Downstream port + output logic downstream_req_o, + input logic downstream_gnt_i, + input logic downstream_rvalid_i, + output logic downstream_we_o, + output logic [3:0] downstream_be_o, + output logic [31:0] downstream_addr_o, + output logic [31:0] downstream_wdata_o, + output logic [6:0] downstream_wdata_intg_o, + input logic [31:0] downstream_rdata_i, + input logic [6:0] downstream_rdata_intg_i, + input logic downstream_err_i, + output logic downstream_tag_o, + input logic downstream_tag_i, + + // Revocation bitmap memory port + output logic revbm_req_o, + input logic revbm_gnt_i, + input logic revbm_rvalid_i, + output logic [31:0] revbm_addr_o, + input logic [31:0] revbm_rdata_i, + input logic [6:0] revbm_rdata_intg_i, + input logic revbm_err_i, + + // Error signals + output logic revbm_data_intg_error_o, + output logic revbm_device_error_o +); + + + /////////// + // Types // + /////////// + + // Expanded exponent (5 bit). Exponent 'd15 stored in memory is mapped to 'd24. + typedef logic [ibex_cheriot_pkg::EXP_W-1:0] cheriot_exp_t; + typedef logic [ibex_cheriot_pkg::BOT_W-1:0] cheriot_bot_t; + typedef logic [ibex_cheriot_pkg::OTYPE_W-1:0] cheriot_otype_t; + typedef logic [ibex_cheriot_pkg::CPERMS_W-1:0] cheriot_cperms_t; + + // Revocation bitmap word address type + typedef logic [RevBitmapAddrWidth-1:0] revbm_addr_t; + + // Local capability metadata type to facilitate parsing of the fields. + typedef struct packed { + cheriot_exp_t exponent; + cheriot_bot_t base; + cheriot_otype_t otype; + cheriot_cperms_t cperms; + } cap_meta_t; + + // Local downstream response type + typedef struct packed { + logic [31:0] data; + logic [6:0] intg; + logic tag; + logic err; + } ds_rsp_t; + + + ///////////// + // Signals // + ///////////// + + // Signals connecting the request to the alignment store. + logic align_fork_valid; + logic align_fork_ready; + + // Alignment store output + logic align_out; + logic align_out_valid; + logic align_out_ready; + + // Pointer store signals + logic [31:0] ptr_store_q; + logic ptr_store_valid_q; + logic ptr_store_enable; + + // Downstream response store output + ds_rsp_t downstream_rsp_in; + ds_rsp_t downstream_rsp_out; + logic downstream_rsp_out_valid; + logic downstream_rsp_out_ready; + + // Base (address) calculation + cap_meta_t cap_meta; + logic unused_cap_meta; + logic is_sealing_cap; + logic [32:0] cap_base_33; + logic unused_cap_base_33; + logic [31:0] cap_base; + cheriot_bot_t addr_mid; + logic [ 2:0] cap_correction; + logic unused_cap_correction; + + // Revocation bitmap addressing + logic [31:0] revbm_cap_addr; + logic [31:0] revbm_bit_addr; + logic [ 4:0] revbm_bit_select; + revbm_addr_t revbm_addr; + logic revbm_out_of_range; + + // Revocation bitmap signals + logic revbm_req_required; + logic revbm_rsp_ready; + logic revbm_outstanding_q; + logic revbm_revoked; + + // Bitmap response ECC signals + logic [1:0] revbm_rsp_data_intg_error; + + + ////////////////////////////// + // Host to Device Intercept // + ////////////////////////////// + + // Both the device and the alignment store need to handshake for + // the stream to advance. + stream_fork #( + .N_OUP(32'd2) + ) u_stream_fork_h2d ( + .clk_i, + .rst_ni, + .valid_i(upstream_req_i), + .ready_o(upstream_gnt_o), + .valid_o({align_fork_valid, downstream_req_o}), + .ready_i({align_fork_ready, downstream_gnt_i}) + ); + + // The host and on lookup the revocation bitmap must handshake to advance the stream + // We use `revbm_req_required` as a section signal here. If there is a bitmap lookup required, + // this signal gets asserted beyond the request handshake completion until the response arrives. + stream_join_dynamic #( + .N_INP(32'd2) + ) u_stream_join_dynamic_d2h ( + .inp_valid_i({revbm_rvalid_i, downstream_rsp_out_valid}), + .inp_ready_o({revbm_rsp_ready, downstream_rsp_out_ready}), + .sel_i ({revbm_req_required, 1'b1 }), + .oup_valid_o(upstream_rvalid_o), + .oup_ready_i(1'b1) + ); + + // Forward OBI payload between host and device + assign downstream_we_o = upstream_we_i; + assign downstream_be_o = upstream_be_i; + assign downstream_addr_o = upstream_addr_i; + assign downstream_wdata_o = upstream_wdata_i; + assign downstream_wdata_intg_o = upstream_wdata_intg_i; + assign upstream_rdata_o = downstream_rsp_out.data; + assign upstream_rdata_intg_o = downstream_rsp_out.intg; + assign upstream_err_o = downstream_rsp_out.err; + + // Forward host to device CHERIoT tag w/o changes + assign downstream_tag_o = upstream_tag_i; + + // Tag handling; always return tag except if we do a revocation bitmap lookup + assign upstream_tag_o = revbm_rvalid_i ? !revbm_revoked : downstream_rsp_out.tag; + + // 64-bit alignment store + prim_fifo_sync #( + .Width(32'd1), + .Pass(1'b0), + .Depth(NumOutstanding), + .NeverClears(1'b1), + .Secure(1'b0) + ) u_prim_fifo_sync_align ( + .clk_i, + .rst_ni, + .clr_i (1'b0), + .wvalid_i(align_fork_valid), + .wready_o(align_fork_ready), + .wdata_i (upstream_addr_i[2]), + .rvalid_o(align_out_valid), + .rready_i(align_out_ready), + .rdata_o (align_out), + .full_o (), + .depth_o (), + .err_o () + ); + + // Element is consumed, if host handshakes response + assign align_out_ready = upstream_rvalid_o; + + + /////////////////// + // Pointer Store // + /////////////////// + + // Pointer buffer is filled iff tag valid & 64-bit aligned + assign ptr_store_enable = downstream_rsp_out.tag && !align_out && align_out_valid && + align_out_ready; + + // Pointer store + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_pointer_store + if(!rst_ni) begin + ptr_store_q <= '0; + end else begin + if (ptr_store_enable) begin + ptr_store_q <= downstream_rsp_out.data; + end + end + end + + // Pointer valid store + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_pointer_vaild_store + if(!rst_ni) begin + ptr_store_valid_q <= 1'b0; + end else begin + if (align_out_ready) begin + ptr_store_valid_q <= ptr_store_enable; + end + end + end + + + ////////////////////////////////// + // Downstream Response Latching // + ////////////////////////////////// + + // downstream response store + prim_fifo_sync #( + .Width($bits(ds_rsp_t)), + .Pass(1'b1), + .Depth(NumOutstanding), + .NeverClears(1'b1), + .Secure(1'b0) + ) u_prim_fifo_ds_rsp_store ( + .clk_i, + .rst_ni, + .clr_i (1'b0), + .wvalid_i(downstream_rvalid_i), + .wready_o(), // Not connected as OBI is not configured to use rready + .wdata_i (downstream_rsp_in), + .rvalid_o(downstream_rsp_out_valid), + .rready_i(downstream_rsp_out_ready), + .rdata_o (downstream_rsp_out), + .full_o (), + .depth_o (), + .err_o () + ); + + assign downstream_rsp_in = '{ + data: downstream_rdata_i, + intg: downstream_rdata_intg_i, + tag: downstream_tag_i, + err: downstream_err_i + }; + + + //////////////////////////////// + // Bitmap Address Calculation // + //////////////////////////////// + + // Exponent is expanded (page 70, CHERIoT Architecture specification, Version 1.0) + assign cap_meta = '{ + base: downstream_rsp_out.data[8:0], + exponent: downstream_rsp_out.data[21:18] != 4'hf ? {1'b0, downstream_rsp_out.data[21:18]} : + 5'd24, + otype: downstream_rsp_out.data[24:22], + cperms: downstream_rsp_out.data[30:25] + }; + + // Not all fields are used + assign unused_cap_meta = ^{cap_meta.otype, cap_meta.cperms[5]}; + + // Check if cap is sealing cap + assign is_sealing_cap = (cap_meta.cperms[4:3] == 2'b00) && (|cap_meta.cperms[2:0]); + + // Extract the middle field from the pointer, bounds depend on exponent, width fixed + assign addr_mid = ptr_store_q[cap_meta.exponent +: ibex_cheriot_pkg::BOT_W]; + + // Fetch the correction values, we are only interested in the base correction value (1 bit) + // top-related inputs are set to zero, top-related outputs ignored + assign cap_correction = ibex_cheriot_pkg::update_temp_fields('0, cap_meta.base, addr_mid); + + // Calculate the base address of the capability as a 33-bit value + assign cap_base_33 = ibex_cheriot_pkg::get_bound33(cap_meta.base, {2{cap_correction[0]}}, + cap_meta.exponent, ptr_store_q); + + // We don't need the correction bits corresponding to the top address + assign unused_cap_correction = ^cap_correction[2:1]; + + // The MSB is unused in our case + assign {unused_cap_base_33, cap_base} = cap_base_33; + + // Address in the revocation bitmap + assign revbm_cap_addr = cap_base - heap_base_addr_i; + + // Bit address in the revocation bitmap (every bit corresponds to one 64-bit capability) + assign revbm_bit_addr = revbm_cap_addr >> 32'd3; + + // Word address of the revocation bitmap + assign revbm_addr = revbm_bit_addr[RevBitmapAddrWidth+5-1:5]; + + // Bit select + assign revbm_bit_select = revbm_bit_addr[4:0]; + + // Capability base is outside of the bitmap range + assign revbm_out_of_range = |(revbm_bit_addr[31:RevBitmapAddrWidth+5]); + + + ////////////////////// + // Bitmap Interface // + ////////////////////// + + // We have loaded valid capability pointer, now we see valid metadata, not a sealing cap, + // and are pointing into the revocation bitmap + assign revbm_req_required = !is_sealing_cap && // Not sealing cap + ptr_store_valid_q && // The base pointer stored is valid + downstream_rsp_out.tag && // We are looking at a capability + downstream_rsp_out_valid && // The stored response is valid + align_out && // We are on the second word of the cap + align_out_valid && // The alignment store is valid + !revbm_out_of_range; // We hit the heap range + + // Assemble read-only request + assign revbm_req_o = revbm_req_required && !revbm_outstanding_q; + assign revbm_addr_o = RevBitmapBaseAddr + {{32 - RevBitmapAddrWidth{1'b0}}, revbm_addr}; + + // Is the current capability marked as revoked? + assign revbm_revoked = revbm_rdata_i[revbm_bit_select] || revbm_err_i; + + // Did we receive a device error? + assign revbm_device_error_o = revbm_rvalid_i && revbm_err_i; + + // Check the bitmap response data integrity + prim_secded_inv_39_32_dec u_prim_secded_inv_39_32_dec_bm_rsp_data ( + .data_i ({revbm_rdata_intg_i, revbm_rdata_i}), + .data_o (), + .syndrome_o(), + .err_o (revbm_rsp_data_intg_error) + ); + + // Mask response integrity error if response is not being handshaked + assign revbm_data_intg_error_o = revbm_rvalid_i && (|revbm_rsp_data_intg_error); + + // One outstanding request + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_rev_req_store + if(!rst_ni) begin + revbm_outstanding_q <= 1'b0; + end else begin + if (revbm_rvalid_i && revbm_rsp_ready) begin + revbm_outstanding_q <= 1'b0; + end else if (revbm_req_o && revbm_gnt_i) begin + revbm_outstanding_q <= 1'b1; + end + end + end + +endmodule diff --git a/vendor/patches/pulp_common_cells/stream_fork_dynamic_sv/0001-replace-assertions.patch b/vendor/patches/pulp_common_cells/stream_fork_dynamic_sv/0001-replace-assertions.patch new file mode 100644 index 000000000..6d6b7533d --- /dev/null +++ b/vendor/patches/pulp_common_cells/stream_fork_dynamic_sv/0001-replace-assertions.patch @@ -0,0 +1,23 @@ +diff --git a/stream_fork_dynamic.sv b/stream_fork_dynamic.sv +index 8da5313d..1aa546b4 100644 +--- a/stream_fork_dynamic.sv ++++ b/stream_fork_dynamic.sv +@@ -11,7 +11,7 @@ + // Authors: + // - Andreas Kurth + +-`include "common_cells/assertions.svh" ++`include "prim_assert.sv" + + /// Dynamic stream fork: Connects the input stream (ready-valid) handshake to a combination of output + /// stream handshake. The combination is determined dynamically through another stream, which +@@ -87,7 +87,6 @@ module stream_fork_dynamic #( + .ready_i ( int_oup_ready ) + ); + +-`ifndef COMMON_CELLS_ASSERTS_OFF +- `ASSERT_INIT(n_oup_0, N_OUP >= 1, "N_OUP must be at least 1!") +-`endif ++ `ASSERT_INIT(NumOutputLargerZero_A, N_OUP >= 1) ++ + endmodule diff --git a/vendor/patches/pulp_common_cells/stream_fork_sv/0001-replace-assertions.patch b/vendor/patches/pulp_common_cells/stream_fork_sv/0001-replace-assertions.patch new file mode 100644 index 000000000..408c39eca --- /dev/null +++ b/vendor/patches/pulp_common_cells/stream_fork_sv/0001-replace-assertions.patch @@ -0,0 +1,23 @@ +diff --git a/stream_fork.sv b/stream_fork.sv +index ebe65f9b..7ae27c1a 100644 +--- a/stream_fork.sv ++++ b/stream_fork.sv +@@ -16,7 +16,7 @@ + // This module has no data ports because stream data does not need to be forked: the data of the + // input stream can just be applied at all output streams. + +-`include "common_cells/assertions.svh" ++`include "prim_assert.sv" + + module stream_fork #( + parameter int unsigned N_OUP = 0 // Synopsys DC requires a default value for parameters. +@@ -124,8 +124,6 @@ module stream_fork #( + assign all_ones = '1; // Synthesis fix for Vivado, which does not correctly compute the width + // of the '1 literal when assigned to a port of parametrized width. + +-`ifndef COMMON_CELLS_ASSERTS_OFF +- `ASSERT_INIT(n_oup_0, N_OUP >= 1, "Number of outputs must be at least 1!") +-`endif ++ `ASSERT_INIT(NumOutputLargerZero_A, N_OUP >= 1) + + endmodule diff --git a/vendor/patches/pulp_common_cells/stream_fork_sv/0002-rename-wait-state.patch b/vendor/patches/pulp_common_cells/stream_fork_sv/0002-rename-wait-state.patch new file mode 100644 index 000000000..b0e07fa66 --- /dev/null +++ b/vendor/patches/pulp_common_cells/stream_fork_sv/0002-rename-wait-state.patch @@ -0,0 +1,46 @@ +diff --git a/stream_fork.sv b/stream_fork.sv +index 7ae27c1a06..e27b6e4549 100644 +--- a/stream_fork.sv ++++ b/stream_fork.sv +@@ -29,7 +29,7 @@ module stream_fork #( + input logic [N_OUP-1:0] ready_i + ); + +- typedef enum logic {READY, WAIT} state_t; ++ typedef enum logic {READY, WAITING} state_t; + + logic [N_OUP-1:0] oup_ready, + all_ones; +@@ -50,13 +50,13 @@ module stream_fork #( + end else begin + ready_o = 1'b0; + // Otherwise, wait for inputs that did not handshake yet. +- inp_state_d = WAIT; ++ inp_state_d = WAITING; + end + end else begin + ready_o = 1'b0; + end + end +- WAIT: begin ++ WAITING: begin + if (valid_i && oup_ready == all_ones) begin + ready_o = 1'b1; + inp_state_d = READY; +@@ -94,14 +94,14 @@ module stream_fork #( + valid_o[i] = 1'b1; + if (ready_i[i]) begin // Output handshake + if (!ready_o) begin // No input handshake yet +- oup_state_d = WAIT; ++ oup_state_d = WAITING; + end + end else begin // No output handshake + oup_ready[i] = 1'b0; + end + end + end +- WAIT: begin ++ WAITING: begin + if (valid_i && ready_o) begin // Input handshake + oup_state_d = READY; + end diff --git a/vendor/patches/pulp_common_cells/stream_join_dynamic_sv/0001-replace-assertions.patch b/vendor/patches/pulp_common_cells/stream_join_dynamic_sv/0001-replace-assertions.patch new file mode 100644 index 000000000..76cbb735c --- /dev/null +++ b/vendor/patches/pulp_common_cells/stream_join_dynamic_sv/0001-replace-assertions.patch @@ -0,0 +1,23 @@ +diff --git a/stream_join_dynamic.sv b/stream_join_dynamic.sv +index 267990b9..0dec8875 100644 +--- a/stream_join_dynamic.sv ++++ b/stream_join_dynamic.sv +@@ -11,7 +11,7 @@ + // Authors: + // - Luca Colagrande + +-`include "common_cells/assertions.svh" ++`include "prim_assert.sv" + + // Stream join dynamic: Joins a parametrizable number of input streams (i.e. valid-ready + // handshaking with dependency rules as in AXI4) to a single output stream. The subset of streams +@@ -39,7 +39,6 @@ module stream_join_dynamic #( + assign inp_ready_o[i] = oup_valid_o & oup_ready_i & sel_i[i]; + end + +-`ifndef COMMON_CELLS_ASSERTS_OFF +- `ASSERT_INIT(n_inp_0, N_INP >= 1, "N_INP must be at least 1!") +-`endif ++ `ASSERT_INIT(NumInputLargerZero_A, N_INP >= 1) ++ + endmodule diff --git a/vendor/pulp_common_cells.core b/vendor/pulp_common_cells.core new file mode 100644 index 000000000..c877e01cb --- /dev/null +++ b/vendor/pulp_common_cells.core @@ -0,0 +1,21 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "pulp-platform:common_cells:common_cells:0.1" +description: "PULP Common Cells" + +filesets: + files_src: + depend: + - lowrisc:prim:assert + files: + - pulp_common_cells/rtl/stream_fork.sv + - pulp_common_cells/rtl/stream_fork_dynamic.sv + - pulp_common_cells/rtl/stream_join_dynamic.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_src diff --git a/vendor/pulp_common_cells.lock.hjson b/vendor/pulp_common_cells.lock.hjson new file mode 100644 index 000000000..df71f1191 --- /dev/null +++ b/vendor/pulp_common_cells.lock.hjson @@ -0,0 +1,14 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This file is generated by the util/vendor script. Please do not modify it +// manually. + +{ + upstream: + { + url: https://github.com/pulp-platform/common_cells.git + rev: 63e1b679a70eca3a1d60d686bc1fa170ec08e1ab + } +} diff --git a/vendor/pulp_common_cells.vendor.hjson b/vendor/pulp_common_cells.vendor.hjson new file mode 100644 index 000000000..2dd98136b --- /dev/null +++ b/vendor/pulp_common_cells.vendor.hjson @@ -0,0 +1,28 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "pulp_common_cells", + target_dir: "pulp_common_cells", + patch_dir: "patches/pulp_common_cells", + + upstream: { + url: "https://github.com/pulp-platform/common_cells.git" + rev: "master" + }, + + mapping: [ + { + from: "src/stream_fork.sv", + to: "rtl/stream_fork.sv", + patch_dir: "stream_fork_sv"}, + { + from: "src/stream_fork_dynamic.sv", + to: "rtl/stream_fork_dynamic.sv", + patch_dir: "stream_fork_dynamic_sv"}, + { + from: "src/stream_join_dynamic.sv", + to: "rtl/stream_join_dynamic.sv", + patch_dir: "stream_join_dynamic_sv"}, + ] +} diff --git a/vendor/pulp_common_cells/rtl/stream_fork.sv b/vendor/pulp_common_cells/rtl/stream_fork.sv new file mode 100644 index 000000000..e27b6e454 --- /dev/null +++ b/vendor/pulp_common_cells/rtl/stream_fork.sv @@ -0,0 +1,129 @@ +// Copyright 2018 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. + +// Stream fork: Connects the input stream (ready-valid) handshake to *all* of `N_OUP` output stream +// handshakes. For each input stream handshake, every output stream handshakes exactly once. The +// input stream only handshakes when all output streams have handshaked, but the output streams do +// not have to handshake simultaneously. +// +// This module has no data ports because stream data does not need to be forked: the data of the +// input stream can just be applied at all output streams. + +`include "prim_assert.sv" + +module stream_fork #( + parameter int unsigned N_OUP = 0 // Synopsys DC requires a default value for parameters. +) ( + input logic clk_i, + input logic rst_ni, + input logic valid_i, + output logic ready_o, + output logic [N_OUP-1:0] valid_o, + input logic [N_OUP-1:0] ready_i +); + + typedef enum logic {READY, WAITING} state_t; + + logic [N_OUP-1:0] oup_ready, + all_ones; + + state_t inp_state_d, inp_state_q; + + // Input control FSM + always_comb begin + // ready_o = 1'b0; + inp_state_d = inp_state_q; + + unique case (inp_state_q) + READY: begin + if (valid_i) begin + if (valid_o == all_ones && ready_i == all_ones) begin + // If handshake on all outputs, handshake on input. + ready_o = 1'b1; + end else begin + ready_o = 1'b0; + // Otherwise, wait for inputs that did not handshake yet. + inp_state_d = WAITING; + end + end else begin + ready_o = 1'b0; + end + end + WAITING: begin + if (valid_i && oup_ready == all_ones) begin + ready_o = 1'b1; + inp_state_d = READY; + end else begin + ready_o = 1'b0; + end + end + default: begin + inp_state_d = READY; + ready_o = 1'b0; + end + endcase + end + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + inp_state_q <= READY; + end else begin + inp_state_q <= inp_state_d; + end + end + + // Output control FSM + for (genvar i = 0; i < N_OUP; i++) begin: gen_oup_state + state_t oup_state_d, oup_state_q; + + always_comb begin + oup_ready[i] = 1'b1; + valid_o[i] = 1'b0; + oup_state_d = oup_state_q; + + unique case (oup_state_q) + READY: begin + if (valid_i) begin + valid_o[i] = 1'b1; + if (ready_i[i]) begin // Output handshake + if (!ready_o) begin // No input handshake yet + oup_state_d = WAITING; + end + end else begin // No output handshake + oup_ready[i] = 1'b0; + end + end + end + WAITING: begin + if (valid_i && ready_o) begin // Input handshake + oup_state_d = READY; + end + end + default: begin + oup_state_d = READY; + end + endcase + end + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + oup_state_q <= READY; + end else begin + oup_state_q <= oup_state_d; + end + end + end + + assign all_ones = '1; // Synthesis fix for Vivado, which does not correctly compute the width + // of the '1 literal when assigned to a port of parametrized width. + + `ASSERT_INIT(NumOutputLargerZero_A, N_OUP >= 1) + +endmodule diff --git a/vendor/pulp_common_cells/rtl/stream_fork_dynamic.sv b/vendor/pulp_common_cells/rtl/stream_fork_dynamic.sv new file mode 100644 index 000000000..1aa546b44 --- /dev/null +++ b/vendor/pulp_common_cells/rtl/stream_fork_dynamic.sv @@ -0,0 +1,92 @@ +// Copyright 2020 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. + +// Authors: +// - Andreas Kurth + +`include "prim_assert.sv" + +/// Dynamic stream fork: Connects the input stream (ready-valid) handshake to a combination of output +/// stream handshake. The combination is determined dynamically through another stream, which +/// provides a bitmask for the fork. For each input stream handshake, every output stream handshakes +/// exactly once. The input stream only handshakes when all output streams have handshaked, but the +/// output streams do not have to handshake simultaneously. +/// +/// This module has no data ports because stream data does not need to be forked: the data of the +/// input stream can just be applied at all output streams. +module stream_fork_dynamic #( + /// Number of output streams + parameter int unsigned N_OUP = 32'd0 // Synopsys DC requires a default value for parameters. +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Input stream valid handshake, + input logic valid_i, + /// Input stream ready handshake + output logic ready_o, + /// Selection mask for the output handshake + input logic [N_OUP-1:0] sel_i, + /// Selection mask valid + input logic sel_valid_i, + /// Selection mask ready + output logic sel_ready_o, + /// Output streams valid handshakes + output logic [N_OUP-1:0] valid_o, + /// Output streams ready handshakes + input logic [N_OUP-1:0] ready_i +); + + logic int_inp_valid, int_inp_ready; + logic [N_OUP-1:0] int_oup_valid, int_oup_ready; + + // Output handshaking + for (genvar i = 0; i < N_OUP; i++) begin : gen_oups + always_comb begin + valid_o[i] = 1'b0; + int_oup_ready[i] = 1'b0; + if (sel_valid_i) begin + if (sel_i[i]) begin + valid_o[i] = int_oup_valid[i]; + int_oup_ready[i] = ready_i[i]; + end else begin + int_oup_ready[i] = 1'b1; + end + end + end + end + + // Input handshaking + always_comb begin + int_inp_valid = 1'b0; + ready_o = 1'b0; + sel_ready_o = 1'b0; + if (sel_valid_i) begin + int_inp_valid = valid_i; + ready_o = int_inp_ready; + sel_ready_o = int_inp_ready; + end + end + + stream_fork #( + .N_OUP ( N_OUP ) + ) i_fork ( + .clk_i, + .rst_ni, + .valid_i ( int_inp_valid ), + .ready_o ( int_inp_ready ), + .valid_o ( int_oup_valid ), + .ready_i ( int_oup_ready ) + ); + + `ASSERT_INIT(NumOutputLargerZero_A, N_OUP >= 1) + +endmodule diff --git a/vendor/pulp_common_cells/rtl/stream_join_dynamic.sv b/vendor/pulp_common_cells/rtl/stream_join_dynamic.sv new file mode 100644 index 000000000..0dec8875e --- /dev/null +++ b/vendor/pulp_common_cells/rtl/stream_join_dynamic.sv @@ -0,0 +1,44 @@ +// Copyright 2020 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. + +// Authors: +// - Luca Colagrande + +`include "prim_assert.sv" + +// Stream join dynamic: Joins a parametrizable number of input streams (i.e. valid-ready +// handshaking with dependency rules as in AXI4) to a single output stream. The subset of streams +// to join can be configured dynamically via `sel_i`. The output handshake happens only after +// there has been a handshake. The data channel flows outside of this module. +module stream_join_dynamic #( + /// Number of input streams + parameter int unsigned N_INP = 32'd0 // Synopsys DC requires a default value for parameters. +) ( + /// Input streams valid handshakes + input logic [N_INP-1:0] inp_valid_i, + /// Input streams ready handshakes + output logic [N_INP-1:0] inp_ready_o, + /// Selection mask for the output handshake + input logic [N_INP-1:0] sel_i, + /// Output stream valid handshake + output logic oup_valid_o, + /// Output stream ready handshake + input logic oup_ready_i +); + + // Corner case when `sel_i` is all 0s should not generate valid + assign oup_valid_o = &(inp_valid_i | ~sel_i) && |sel_i; + for (genvar i = 0; i < N_INP; i++) begin : gen_inp_ready + assign inp_ready_o[i] = oup_valid_o & oup_ready_i & sel_i[i]; + end + + `ASSERT_INIT(NumInputLargerZero_A, N_INP >= 1) + +endmodule