diff --git a/hw/ip/i2c/data/i2c.hjson b/hw/ip/i2c/data/i2c.hjson index ce9015c936538..c9c20aa3f0df1 100644 --- a/hw/ip/i2c/data/i2c.hjson +++ b/hw/ip/i2c/data/i2c.hjson @@ -30,7 +30,7 @@ ] clocking: [{clock: "clk_i", reset: "rst_ni"}], bus_interfaces: [ - { protocol: "tlul", direction: "device" } + { protocol: "tlul", direction: "device", racl_support: true } ], // INPUT pins available_inout_list: [ @@ -130,6 +130,36 @@ ''' act: "req" } + { struct: "racl_policy_vec", + type: "uni", + name: "racl_policies", + act: "rcv", + package: "top_racl_pkg", + desc: ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + } + { struct: "logic", + type: "uni", + name: "racl_error", + act: "req", + width : "1", + desc: ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + } + { struct: "racl_error_log", + type: "uni", + name: "racl_error_log", + act: "req", + width: "1" + package: "top_racl_pkg", + desc: ''' + RACL error log information of this module. + ''' + } ] param_list: [ { name: "FifoDepth", diff --git a/hw/ip/i2c/doc/interfaces.md b/hw/ip/i2c/doc/interfaces.md index 546c45c828052..c5435c59b18d4 100644 --- a/hw/ip/i2c/doc/interfaces.md +++ b/hw/ip/i2c/doc/interfaces.md @@ -16,12 +16,15 @@ Referring to the [Comportable guideline for peripheral device functionality](htt ## [Inter-Module Signals](https://opentitan.org/book/doc/contributing/hw/comportability/index.html#inter-signal-handling) -| Port Name | Package::Struct | Type | Act | Width | Description | -|:-------------|:--------------------------------|:--------|:------|--------:|:-----------------------------------------------------------------------------------------------------------------------------------------| -| ram_cfg | prim_ram_1p_pkg::ram_1p_cfg | uni | rcv | 1 | | -| ram_cfg_rsp | prim_ram_1p_pkg::ram_1p_cfg_rsp | uni | req | 1 | | -| lsio_trigger | logic | uni | req | 1 | Self-clearing status trigger for the DMA. Set when RX TX FIFO is past their configured watermark matching watermark interrupt behaviour. | -| tl | tlul_pkg::tl | req_rsp | rsp | 1 | | +| Port Name | Package::Struct | Type | Act | Width | Description | +|:---------------|:--------------------------------|:--------|:------|--------:|:-----------------------------------------------------------------------------------------------------------------------------------------| +| ram_cfg | prim_ram_1p_pkg::ram_1p_cfg | uni | rcv | 1 | | +| ram_cfg_rsp | prim_ram_1p_pkg::ram_1p_cfg_rsp | uni | req | 1 | | +| lsio_trigger | logic | uni | req | 1 | Self-clearing status trigger for the DMA. Set when RX TX FIFO is past their configured watermark matching watermark interrupt behaviour. | +| racl_policies | top_racl_pkg::racl_policy_vec | uni | rcv | 1 | Incoming RACL policy vector from a racl_ctrl instance. The policy selection vector (parameter) selects the policy for each register. | +| racl_error | logic | uni | req | 1 | RACL error indication signal. If 1, the error log contains valid information. | +| racl_error_log | top_racl_pkg::racl_error_log | uni | req | 1 | RACL error log information of this module. | +| tl | tlul_pkg::tl | req_rsp | rsp | 1 | | ## Interrupts diff --git a/hw/ip/i2c/i2c.core b/hw/ip/i2c/i2c.core index 4ec62eb5fee07..316ac35235fcd 100644 --- a/hw/ip/i2c/i2c.core +++ b/hw/ip/i2c/i2c.core @@ -13,6 +13,7 @@ filesets: - lowrisc:prim:ram_1p_adv - lowrisc:ip:tlul - lowrisc:ip:i2c_pkg + - lowrisc:systems:top_racl_pkg files: - rtl/i2c_reg_pkg.sv - rtl/i2c_reg_top.sv diff --git a/hw/ip/i2c/rtl/i2c.sv b/hw/ip/i2c/rtl/i2c.sv index e29e126867adf..cfe20503cc98d 100644 --- a/hw/ip/i2c/rtl/i2c.sv +++ b/hw/ip/i2c/rtl/i2c.sv @@ -9,8 +9,11 @@ module i2c import i2c_reg_pkg::*; #( - parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, - parameter int unsigned InputDelayCycles = 0 + parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + parameter int unsigned InputDelayCycles = 0, + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 1'b1, + parameter int unsigned RaclPolicySelVec[32] = '{32{0}} ) ( input clk_i, input rst_ni, @@ -25,6 +28,11 @@ module i2c input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, + // Generic IO input cio_scl_i, output logic cio_scl_o, @@ -58,13 +66,20 @@ module i2c logic [NumAlerts-1:0] alert_test, alerts; - i2c_reg_top u_reg ( + i2c_reg_top #( + .EnableRacl(EnableRacl), + .RaclErrorRsp(RaclErrorRsp), + .RaclPolicySelVec(RaclPolicySelVec) + ) u_reg ( .clk_i, .rst_ni, .tl_i, .tl_o, .reg2hw, .hw2reg, + .racl_policies_i, + .racl_error_o, + .racl_error_log_o, // SEC_CM: BUS.INTEGRITY .intg_err_o(alerts[0]) ); diff --git a/hw/ip/i2c/rtl/i2c_reg_top.sv b/hw/ip/i2c/rtl/i2c_reg_top.sv index 4de69b9f4fbeb..75a8c54e39c96 100644 --- a/hw/ip/i2c/rtl/i2c_reg_top.sv +++ b/hw/ip/i2c/rtl/i2c_reg_top.sv @@ -6,7 +6,12 @@ `include "prim_assert.sv" -module i2c_reg_top ( +module i2c_reg_top + # ( + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 1'b1, + parameter int unsigned RaclPolicySelVec[32] = '{32{0}} + ) ( input clk_i, input rst_ni, input tlul_pkg::tl_h2d_t tl_i, @@ -15,6 +20,11 @@ module i2c_reg_top ( output i2c_reg_pkg::i2c_reg2hw_t reg2hw, // Write input i2c_reg_pkg::i2c_hw2reg_t hw2reg, // Read + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, + // Integrity check errors output logic intg_err_o ); @@ -110,7 +120,8 @@ module i2c_reg_top ( .be_o (reg_be), .busy_i (reg_busy), .rdata_i (reg_rdata), - .error_i (reg_error) + // Translate RACL error to TLUL error if enabled + .error_i (reg_error | (RaclErrorRsp & racl_error_o)) ); // cdc oversampling signals @@ -3385,8 +3396,32 @@ module i2c_reg_top ( logic [31:0] addr_hit; + top_racl_pkg::racl_role_vec_t racl_role_vec; + top_racl_pkg::racl_role_t racl_role; + + logic [31:0] racl_addr_hit_read; + logic [31:0] racl_addr_hit_write; + + if (EnableRacl) begin : gen_racl_role_logic + // Retrieve RACL role from user bits and one-hot encode that for the comparison bitmap + assign racl_role = top_racl_pkg::tlul_extract_racl_role_bits(tl_i.a_user.rsvd); + + prim_onehot_enc #( + .OneHotWidth( $bits(top_racl_pkg::racl_role_vec_t) ) + ) u_racl_role_encode ( + .in_i ( racl_role ), + .en_i ( 1'b1 ), + .out_o( racl_role_vec ) + ); + end else begin : gen_no_racl_role_logic + assign racl_role = '0; + assign racl_role_vec = '0; + end + always_comb begin addr_hit = '0; + racl_addr_hit_read = '0; + racl_addr_hit_write = '0; addr_hit[ 0] = (reg_addr == I2C_INTR_STATE_OFFSET); addr_hit[ 1] = (reg_addr == I2C_INTR_ENABLE_OFFSET); addr_hit[ 2] = (reg_addr == I2C_INTR_TEST_OFFSET); @@ -3419,49 +3454,74 @@ module i2c_reg_top ( addr_hit[29] = (reg_addr == I2C_HOST_NACK_HANDLER_TIMEOUT_OFFSET); addr_hit[30] = (reg_addr == I2C_CONTROLLER_EVENTS_OFFSET); addr_hit[31] = (reg_addr == I2C_TARGET_EVENTS_OFFSET); + + if (EnableRacl) begin : gen_racl_hit + for (int unsigned slice_idx = 0; slice_idx < 32; slice_idx++) begin + racl_addr_hit_read[slice_idx] = + addr_hit[slice_idx] & (|(racl_policies_i[RaclPolicySelVec[slice_idx]].read_perm + & racl_role_vec)); + racl_addr_hit_write[slice_idx] = + addr_hit[slice_idx] & (|(racl_policies_i[RaclPolicySelVec[slice_idx]].write_perm + & racl_role_vec)); + end + end else begin : gen_no_racl + racl_addr_hit_read = addr_hit; + racl_addr_hit_write = addr_hit; + end end assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + // Address hit but failed the RACL check + assign racl_error_o = (|addr_hit) & ~(|(addr_hit & (racl_addr_hit_read | racl_addr_hit_write))); + assign racl_error_log_o.racl_role = racl_role; + + if (EnableRacl) begin : gen_racl_log + assign racl_error_log_o.ctn_uid = top_racl_pkg::tlul_extract_ctn_uid_bits(tl_i.a_user.rsvd); + assign racl_error_log_o.read_access = tl_i.a_opcode == tlul_pkg::Get; + end else begin : gen_no_racl_log + assign racl_error_log_o.ctn_uid = '0; + assign racl_error_log_o.read_access = 1'b0; + end // Check sub-word write is permitted always_comb begin wr_err = (reg_we & - ((addr_hit[ 0] & (|(I2C_PERMIT[ 0] & ~reg_be))) | - (addr_hit[ 1] & (|(I2C_PERMIT[ 1] & ~reg_be))) | - (addr_hit[ 2] & (|(I2C_PERMIT[ 2] & ~reg_be))) | - (addr_hit[ 3] & (|(I2C_PERMIT[ 3] & ~reg_be))) | - (addr_hit[ 4] & (|(I2C_PERMIT[ 4] & ~reg_be))) | - (addr_hit[ 5] & (|(I2C_PERMIT[ 5] & ~reg_be))) | - (addr_hit[ 6] & (|(I2C_PERMIT[ 6] & ~reg_be))) | - (addr_hit[ 7] & (|(I2C_PERMIT[ 7] & ~reg_be))) | - (addr_hit[ 8] & (|(I2C_PERMIT[ 8] & ~reg_be))) | - (addr_hit[ 9] & (|(I2C_PERMIT[ 9] & ~reg_be))) | - (addr_hit[10] & (|(I2C_PERMIT[10] & ~reg_be))) | - (addr_hit[11] & (|(I2C_PERMIT[11] & ~reg_be))) | - (addr_hit[12] & (|(I2C_PERMIT[12] & ~reg_be))) | - (addr_hit[13] & (|(I2C_PERMIT[13] & ~reg_be))) | - (addr_hit[14] & (|(I2C_PERMIT[14] & ~reg_be))) | - (addr_hit[15] & (|(I2C_PERMIT[15] & ~reg_be))) | - (addr_hit[16] & (|(I2C_PERMIT[16] & ~reg_be))) | - (addr_hit[17] & (|(I2C_PERMIT[17] & ~reg_be))) | - (addr_hit[18] & (|(I2C_PERMIT[18] & ~reg_be))) | - (addr_hit[19] & (|(I2C_PERMIT[19] & ~reg_be))) | - (addr_hit[20] & (|(I2C_PERMIT[20] & ~reg_be))) | - (addr_hit[21] & (|(I2C_PERMIT[21] & ~reg_be))) | - (addr_hit[22] & (|(I2C_PERMIT[22] & ~reg_be))) | - (addr_hit[23] & (|(I2C_PERMIT[23] & ~reg_be))) | - (addr_hit[24] & (|(I2C_PERMIT[24] & ~reg_be))) | - (addr_hit[25] & (|(I2C_PERMIT[25] & ~reg_be))) | - (addr_hit[26] & (|(I2C_PERMIT[26] & ~reg_be))) | - (addr_hit[27] & (|(I2C_PERMIT[27] & ~reg_be))) | - (addr_hit[28] & (|(I2C_PERMIT[28] & ~reg_be))) | - (addr_hit[29] & (|(I2C_PERMIT[29] & ~reg_be))) | - (addr_hit[30] & (|(I2C_PERMIT[30] & ~reg_be))) | - (addr_hit[31] & (|(I2C_PERMIT[31] & ~reg_be))))); + ((racl_addr_hit_write[ 0] & (|(I2C_PERMIT[ 0] & ~reg_be))) | + (racl_addr_hit_write[ 1] & (|(I2C_PERMIT[ 1] & ~reg_be))) | + (racl_addr_hit_write[ 2] & (|(I2C_PERMIT[ 2] & ~reg_be))) | + (racl_addr_hit_write[ 3] & (|(I2C_PERMIT[ 3] & ~reg_be))) | + (racl_addr_hit_write[ 4] & (|(I2C_PERMIT[ 4] & ~reg_be))) | + (racl_addr_hit_write[ 5] & (|(I2C_PERMIT[ 5] & ~reg_be))) | + (racl_addr_hit_write[ 6] & (|(I2C_PERMIT[ 6] & ~reg_be))) | + (racl_addr_hit_write[ 7] & (|(I2C_PERMIT[ 7] & ~reg_be))) | + (racl_addr_hit_write[ 8] & (|(I2C_PERMIT[ 8] & ~reg_be))) | + (racl_addr_hit_write[ 9] & (|(I2C_PERMIT[ 9] & ~reg_be))) | + (racl_addr_hit_write[10] & (|(I2C_PERMIT[10] & ~reg_be))) | + (racl_addr_hit_write[11] & (|(I2C_PERMIT[11] & ~reg_be))) | + (racl_addr_hit_write[12] & (|(I2C_PERMIT[12] & ~reg_be))) | + (racl_addr_hit_write[13] & (|(I2C_PERMIT[13] & ~reg_be))) | + (racl_addr_hit_write[14] & (|(I2C_PERMIT[14] & ~reg_be))) | + (racl_addr_hit_write[15] & (|(I2C_PERMIT[15] & ~reg_be))) | + (racl_addr_hit_write[16] & (|(I2C_PERMIT[16] & ~reg_be))) | + (racl_addr_hit_write[17] & (|(I2C_PERMIT[17] & ~reg_be))) | + (racl_addr_hit_write[18] & (|(I2C_PERMIT[18] & ~reg_be))) | + (racl_addr_hit_write[19] & (|(I2C_PERMIT[19] & ~reg_be))) | + (racl_addr_hit_write[20] & (|(I2C_PERMIT[20] & ~reg_be))) | + (racl_addr_hit_write[21] & (|(I2C_PERMIT[21] & ~reg_be))) | + (racl_addr_hit_write[22] & (|(I2C_PERMIT[22] & ~reg_be))) | + (racl_addr_hit_write[23] & (|(I2C_PERMIT[23] & ~reg_be))) | + (racl_addr_hit_write[24] & (|(I2C_PERMIT[24] & ~reg_be))) | + (racl_addr_hit_write[25] & (|(I2C_PERMIT[25] & ~reg_be))) | + (racl_addr_hit_write[26] & (|(I2C_PERMIT[26] & ~reg_be))) | + (racl_addr_hit_write[27] & (|(I2C_PERMIT[27] & ~reg_be))) | + (racl_addr_hit_write[28] & (|(I2C_PERMIT[28] & ~reg_be))) | + (racl_addr_hit_write[29] & (|(I2C_PERMIT[29] & ~reg_be))) | + (racl_addr_hit_write[30] & (|(I2C_PERMIT[30] & ~reg_be))) | + (racl_addr_hit_write[31] & (|(I2C_PERMIT[31] & ~reg_be))))); end // Generate write-enables - assign intr_state_we = addr_hit[0] & reg_we & !reg_error; + assign intr_state_we = racl_addr_hit_write[0] & reg_we & !reg_error; assign intr_state_rx_overflow_wd = reg_wdata[3]; @@ -3478,7 +3538,7 @@ module i2c_reg_top ( assign intr_state_unexp_stop_wd = reg_wdata[13]; assign intr_state_host_timeout_wd = reg_wdata[14]; - assign intr_enable_we = addr_hit[1] & reg_we & !reg_error; + assign intr_enable_we = racl_addr_hit_write[1] & reg_we & !reg_error; assign intr_enable_fmt_threshold_wd = reg_wdata[0]; @@ -3509,7 +3569,7 @@ module i2c_reg_top ( assign intr_enable_unexp_stop_wd = reg_wdata[13]; assign intr_enable_host_timeout_wd = reg_wdata[14]; - assign intr_test_we = addr_hit[2] & reg_we & !reg_error; + assign intr_test_we = racl_addr_hit_write[2] & reg_we & !reg_error; assign intr_test_fmt_threshold_wd = reg_wdata[0]; @@ -3540,10 +3600,10 @@ module i2c_reg_top ( assign intr_test_unexp_stop_wd = reg_wdata[13]; assign intr_test_host_timeout_wd = reg_wdata[14]; - assign alert_test_we = addr_hit[3] & reg_we & !reg_error; + assign alert_test_we = racl_addr_hit_write[3] & reg_we & !reg_error; assign alert_test_wd = reg_wdata[0]; - assign ctrl_we = addr_hit[4] & reg_we & !reg_error; + assign ctrl_we = racl_addr_hit_write[4] & reg_we & !reg_error; assign ctrl_enablehost_wd = reg_wdata[0]; @@ -3558,9 +3618,9 @@ module i2c_reg_top ( assign ctrl_multi_controller_monitor_en_wd = reg_wdata[5]; assign ctrl_tx_stretch_ctrl_en_wd = reg_wdata[6]; - assign status_re = addr_hit[5] & reg_re & !reg_error; - assign rdata_re = addr_hit[6] & reg_re & !reg_error; - assign fdata_we = addr_hit[7] & reg_we & !reg_error; + assign status_re = racl_addr_hit_write[5] & reg_re & !reg_error; + assign rdata_re = racl_addr_hit_write[6] & reg_re & !reg_error; + assign fdata_we = racl_addr_hit_write[7] & reg_we & !reg_error; assign fdata_fbyte_wd = reg_wdata[7:0]; @@ -3573,7 +3633,7 @@ module i2c_reg_top ( assign fdata_rcont_wd = reg_wdata[11]; assign fdata_nakok_wd = reg_wdata[12]; - assign fifo_ctrl_we = addr_hit[8] & reg_we & !reg_error; + assign fifo_ctrl_we = racl_addr_hit_write[8] & reg_we & !reg_error; assign fifo_ctrl_rxrst_wd = reg_wdata[0]; @@ -3582,59 +3642,59 @@ module i2c_reg_top ( assign fifo_ctrl_acqrst_wd = reg_wdata[7]; assign fifo_ctrl_txrst_wd = reg_wdata[8]; - assign host_fifo_config_we = addr_hit[9] & reg_we & !reg_error; + assign host_fifo_config_we = racl_addr_hit_write[9] & reg_we & !reg_error; assign host_fifo_config_rx_thresh_wd = reg_wdata[11:0]; assign host_fifo_config_fmt_thresh_wd = reg_wdata[27:16]; - assign target_fifo_config_we = addr_hit[10] & reg_we & !reg_error; + assign target_fifo_config_we = racl_addr_hit_write[10] & reg_we & !reg_error; assign target_fifo_config_tx_thresh_wd = reg_wdata[11:0]; assign target_fifo_config_acq_thresh_wd = reg_wdata[27:16]; - assign host_fifo_status_re = addr_hit[11] & reg_re & !reg_error; - assign target_fifo_status_re = addr_hit[12] & reg_re & !reg_error; - assign ovrd_we = addr_hit[13] & reg_we & !reg_error; + assign host_fifo_status_re = racl_addr_hit_write[11] & reg_re & !reg_error; + assign target_fifo_status_re = racl_addr_hit_write[12] & reg_re & !reg_error; + assign ovrd_we = racl_addr_hit_write[13] & reg_we & !reg_error; assign ovrd_txovrden_wd = reg_wdata[0]; assign ovrd_sclval_wd = reg_wdata[1]; assign ovrd_sdaval_wd = reg_wdata[2]; - assign val_re = addr_hit[14] & reg_re & !reg_error; - assign timing0_we = addr_hit[15] & reg_we & !reg_error; + assign val_re = racl_addr_hit_write[14] & reg_re & !reg_error; + assign timing0_we = racl_addr_hit_write[15] & reg_we & !reg_error; assign timing0_thigh_wd = reg_wdata[12:0]; assign timing0_tlow_wd = reg_wdata[28:16]; - assign timing1_we = addr_hit[16] & reg_we & !reg_error; + assign timing1_we = racl_addr_hit_write[16] & reg_we & !reg_error; assign timing1_t_r_wd = reg_wdata[9:0]; assign timing1_t_f_wd = reg_wdata[24:16]; - assign timing2_we = addr_hit[17] & reg_we & !reg_error; + assign timing2_we = racl_addr_hit_write[17] & reg_we & !reg_error; assign timing2_tsu_sta_wd = reg_wdata[12:0]; assign timing2_thd_sta_wd = reg_wdata[28:16]; - assign timing3_we = addr_hit[18] & reg_we & !reg_error; + assign timing3_we = racl_addr_hit_write[18] & reg_we & !reg_error; assign timing3_tsu_dat_wd = reg_wdata[8:0]; assign timing3_thd_dat_wd = reg_wdata[28:16]; - assign timing4_we = addr_hit[19] & reg_we & !reg_error; + assign timing4_we = racl_addr_hit_write[19] & reg_we & !reg_error; assign timing4_tsu_sto_wd = reg_wdata[12:0]; assign timing4_t_buf_wd = reg_wdata[28:16]; - assign timeout_ctrl_we = addr_hit[20] & reg_we & !reg_error; + assign timeout_ctrl_we = racl_addr_hit_write[20] & reg_we & !reg_error; assign timeout_ctrl_val_wd = reg_wdata[29:0]; assign timeout_ctrl_mode_wd = reg_wdata[30]; assign timeout_ctrl_en_wd = reg_wdata[31]; - assign target_id_we = addr_hit[21] & reg_we & !reg_error; + assign target_id_we = racl_addr_hit_write[21] & reg_we & !reg_error; assign target_id_address0_wd = reg_wdata[6:0]; @@ -3643,34 +3703,34 @@ module i2c_reg_top ( assign target_id_address1_wd = reg_wdata[20:14]; assign target_id_mask1_wd = reg_wdata[27:21]; - assign acqdata_re = addr_hit[22] & reg_re & !reg_error; - assign txdata_we = addr_hit[23] & reg_we & !reg_error; + assign acqdata_re = racl_addr_hit_write[22] & reg_re & !reg_error; + assign txdata_we = racl_addr_hit_write[23] & reg_we & !reg_error; assign txdata_wd = reg_wdata[7:0]; - assign host_timeout_ctrl_we = addr_hit[24] & reg_we & !reg_error; + assign host_timeout_ctrl_we = racl_addr_hit_write[24] & reg_we & !reg_error; assign host_timeout_ctrl_wd = reg_wdata[19:0]; - assign target_timeout_ctrl_we = addr_hit[25] & reg_we & !reg_error; + assign target_timeout_ctrl_we = racl_addr_hit_write[25] & reg_we & !reg_error; assign target_timeout_ctrl_val_wd = reg_wdata[30:0]; assign target_timeout_ctrl_en_wd = reg_wdata[31]; - assign target_nack_count_re = addr_hit[26] & reg_re & !reg_error; + assign target_nack_count_re = racl_addr_hit_write[26] & reg_re & !reg_error; assign target_nack_count_wd = '1; - assign target_ack_ctrl_re = addr_hit[27] & reg_re & !reg_error; - assign target_ack_ctrl_we = addr_hit[27] & reg_we & !reg_error; + assign target_ack_ctrl_re = racl_addr_hit_write[27] & reg_re & !reg_error; + assign target_ack_ctrl_we = racl_addr_hit_write[27] & reg_we & !reg_error; assign target_ack_ctrl_nbytes_wd = reg_wdata[8:0]; assign target_ack_ctrl_nack_wd = reg_wdata[31]; - assign acq_fifo_next_data_re = addr_hit[28] & reg_re & !reg_error; - assign host_nack_handler_timeout_we = addr_hit[29] & reg_we & !reg_error; + assign acq_fifo_next_data_re = racl_addr_hit_write[28] & reg_re & !reg_error; + assign host_nack_handler_timeout_we = racl_addr_hit_write[29] & reg_we & !reg_error; assign host_nack_handler_timeout_val_wd = reg_wdata[30:0]; assign host_nack_handler_timeout_en_wd = reg_wdata[31]; - assign controller_events_we = addr_hit[30] & reg_we & !reg_error; + assign controller_events_we = racl_addr_hit_write[30] & reg_we & !reg_error; assign controller_events_nack_wd = reg_wdata[0]; @@ -3679,7 +3739,7 @@ module i2c_reg_top ( assign controller_events_bus_timeout_wd = reg_wdata[2]; assign controller_events_arbitration_lost_wd = reg_wdata[3]; - assign target_events_we = addr_hit[31] & reg_we & !reg_error; + assign target_events_we = racl_addr_hit_write[31] & reg_we & !reg_error; assign target_events_tx_pending_wd = reg_wdata[0]; @@ -3728,7 +3788,7 @@ module i2c_reg_top ( always_comb begin reg_rdata_next = '0; unique case (1'b1) - addr_hit[0]: begin + racl_addr_hit_read[0]: begin reg_rdata_next[0] = intr_state_fmt_threshold_qs; reg_rdata_next[1] = intr_state_rx_threshold_qs; reg_rdata_next[2] = intr_state_acq_threshold_qs; @@ -3746,7 +3806,7 @@ module i2c_reg_top ( reg_rdata_next[14] = intr_state_host_timeout_qs; end - addr_hit[1]: begin + racl_addr_hit_read[1]: begin reg_rdata_next[0] = intr_enable_fmt_threshold_qs; reg_rdata_next[1] = intr_enable_rx_threshold_qs; reg_rdata_next[2] = intr_enable_acq_threshold_qs; @@ -3764,7 +3824,7 @@ module i2c_reg_top ( reg_rdata_next[14] = intr_enable_host_timeout_qs; end - addr_hit[2]: begin + racl_addr_hit_read[2]: begin reg_rdata_next[0] = '0; reg_rdata_next[1] = '0; reg_rdata_next[2] = '0; @@ -3782,11 +3842,11 @@ module i2c_reg_top ( reg_rdata_next[14] = '0; end - addr_hit[3]: begin + racl_addr_hit_read[3]: begin reg_rdata_next[0] = '0; end - addr_hit[4]: begin + racl_addr_hit_read[4]: begin reg_rdata_next[0] = ctrl_enablehost_qs; reg_rdata_next[1] = ctrl_enabletarget_qs; reg_rdata_next[2] = ctrl_llpbk_qs; @@ -3796,7 +3856,7 @@ module i2c_reg_top ( reg_rdata_next[6] = ctrl_tx_stretch_ctrl_en_qs; end - addr_hit[5]: begin + racl_addr_hit_read[5]: begin reg_rdata_next[0] = status_fmtfull_qs; reg_rdata_next[1] = status_rxfull_qs; reg_rdata_next[2] = status_fmtempty_qs; @@ -3810,11 +3870,11 @@ module i2c_reg_top ( reg_rdata_next[10] = status_ack_ctrl_stretch_qs; end - addr_hit[6]: begin + racl_addr_hit_read[6]: begin reg_rdata_next[7:0] = rdata_qs; end - addr_hit[7]: begin + racl_addr_hit_read[7]: begin reg_rdata_next[7:0] = '0; reg_rdata_next[8] = '0; reg_rdata_next[9] = '0; @@ -3823,126 +3883,126 @@ module i2c_reg_top ( reg_rdata_next[12] = '0; end - addr_hit[8]: begin + racl_addr_hit_read[8]: begin reg_rdata_next[0] = '0; reg_rdata_next[1] = '0; reg_rdata_next[7] = '0; reg_rdata_next[8] = '0; end - addr_hit[9]: begin + racl_addr_hit_read[9]: begin reg_rdata_next[11:0] = host_fifo_config_rx_thresh_qs; reg_rdata_next[27:16] = host_fifo_config_fmt_thresh_qs; end - addr_hit[10]: begin + racl_addr_hit_read[10]: begin reg_rdata_next[11:0] = target_fifo_config_tx_thresh_qs; reg_rdata_next[27:16] = target_fifo_config_acq_thresh_qs; end - addr_hit[11]: begin + racl_addr_hit_read[11]: begin reg_rdata_next[11:0] = host_fifo_status_fmtlvl_qs; reg_rdata_next[27:16] = host_fifo_status_rxlvl_qs; end - addr_hit[12]: begin + racl_addr_hit_read[12]: begin reg_rdata_next[11:0] = target_fifo_status_txlvl_qs; reg_rdata_next[27:16] = target_fifo_status_acqlvl_qs; end - addr_hit[13]: begin + racl_addr_hit_read[13]: begin reg_rdata_next[0] = ovrd_txovrden_qs; reg_rdata_next[1] = ovrd_sclval_qs; reg_rdata_next[2] = ovrd_sdaval_qs; end - addr_hit[14]: begin + racl_addr_hit_read[14]: begin reg_rdata_next[15:0] = val_scl_rx_qs; reg_rdata_next[31:16] = val_sda_rx_qs; end - addr_hit[15]: begin + racl_addr_hit_read[15]: begin reg_rdata_next[12:0] = timing0_thigh_qs; reg_rdata_next[28:16] = timing0_tlow_qs; end - addr_hit[16]: begin + racl_addr_hit_read[16]: begin reg_rdata_next[9:0] = timing1_t_r_qs; reg_rdata_next[24:16] = timing1_t_f_qs; end - addr_hit[17]: begin + racl_addr_hit_read[17]: begin reg_rdata_next[12:0] = timing2_tsu_sta_qs; reg_rdata_next[28:16] = timing2_thd_sta_qs; end - addr_hit[18]: begin + racl_addr_hit_read[18]: begin reg_rdata_next[8:0] = timing3_tsu_dat_qs; reg_rdata_next[28:16] = timing3_thd_dat_qs; end - addr_hit[19]: begin + racl_addr_hit_read[19]: begin reg_rdata_next[12:0] = timing4_tsu_sto_qs; reg_rdata_next[28:16] = timing4_t_buf_qs; end - addr_hit[20]: begin + racl_addr_hit_read[20]: begin reg_rdata_next[29:0] = timeout_ctrl_val_qs; reg_rdata_next[30] = timeout_ctrl_mode_qs; reg_rdata_next[31] = timeout_ctrl_en_qs; end - addr_hit[21]: begin + racl_addr_hit_read[21]: begin reg_rdata_next[6:0] = target_id_address0_qs; reg_rdata_next[13:7] = target_id_mask0_qs; reg_rdata_next[20:14] = target_id_address1_qs; reg_rdata_next[27:21] = target_id_mask1_qs; end - addr_hit[22]: begin + racl_addr_hit_read[22]: begin reg_rdata_next[7:0] = acqdata_abyte_qs; reg_rdata_next[10:8] = acqdata_signal_qs; end - addr_hit[23]: begin + racl_addr_hit_read[23]: begin reg_rdata_next[7:0] = '0; end - addr_hit[24]: begin + racl_addr_hit_read[24]: begin reg_rdata_next[19:0] = host_timeout_ctrl_qs; end - addr_hit[25]: begin + racl_addr_hit_read[25]: begin reg_rdata_next[30:0] = target_timeout_ctrl_val_qs; reg_rdata_next[31] = target_timeout_ctrl_en_qs; end - addr_hit[26]: begin + racl_addr_hit_read[26]: begin reg_rdata_next[7:0] = target_nack_count_qs; end - addr_hit[27]: begin + racl_addr_hit_read[27]: begin reg_rdata_next[8:0] = target_ack_ctrl_nbytes_qs; reg_rdata_next[31] = '0; end - addr_hit[28]: begin + racl_addr_hit_read[28]: begin reg_rdata_next[7:0] = acq_fifo_next_data_qs; end - addr_hit[29]: begin + racl_addr_hit_read[29]: begin reg_rdata_next[30:0] = host_nack_handler_timeout_val_qs; reg_rdata_next[31] = host_nack_handler_timeout_en_qs; end - addr_hit[30]: begin + racl_addr_hit_read[30]: begin reg_rdata_next[0] = controller_events_nack_qs; reg_rdata_next[1] = controller_events_unhandled_nack_timeout_qs; reg_rdata_next[2] = controller_events_bus_timeout_qs; reg_rdata_next[3] = controller_events_arbitration_lost_qs; end - addr_hit[31]: begin + racl_addr_hit_read[31]: begin reg_rdata_next[0] = target_events_tx_pending_qs; reg_rdata_next[1] = target_events_bus_timeout_qs; reg_rdata_next[2] = target_events_arbitration_lost_qs; @@ -3969,6 +4029,8 @@ module i2c_reg_top ( logic unused_be; assign unused_wdata = ^reg_wdata; assign unused_be = ^reg_be; + logic unused_policy_sel; + assign unused_policy_sel = ^racl_policies_i; // Assertions for Register Interface `ASSERT_PULSE(wePulse, reg_we, clk_i, !rst_ni) diff --git a/hw/ip/pwm/data/pwm.hjson b/hw/ip/pwm/data/pwm.hjson index ef3c6057aec77..af8e7a5aa3f33 100644 --- a/hw/ip/pwm/data/pwm.hjson +++ b/hw/ip/pwm/data/pwm.hjson @@ -31,7 +31,7 @@ {clock: "clk_core_i", reset: "rst_core_ni"} ] bus_interfaces: [ - { protocol: "tlul", direction: "device" } + { protocol: "tlul", direction: "device", racl_support: true } ], regwidth: "32", param_list: [ @@ -87,6 +87,38 @@ desc: "End-to-end bus integrity scheme." } ] + inter_signal_list: [ + { struct: "racl_policy_vec", + type: "uni", + name: "racl_policies", + act: "rcv", + package: "top_racl_pkg", + desc: ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + } + { struct: "logic", + type: "uni", + name: "racl_error", + act: "req", + width : "1", + desc: ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + } + { struct: "racl_error_log", + type: "uni", + name: "racl_error_log", + act: "req", + width: "1" + package: "top_racl_pkg", + desc: ''' + RACL error log information of this module. + ''' + } + ], registers: [ { name: "REGWEN", desc: "Register write enable for all control registers", diff --git a/hw/ip/pwm/doc/interfaces.md b/hw/ip/pwm/doc/interfaces.md index bb3ada272623a..b9b4beb55647b 100644 --- a/hw/ip/pwm/doc/interfaces.md +++ b/hw/ip/pwm/doc/interfaces.md @@ -16,9 +16,12 @@ Referring to the [Comportable guideline for peripheral device functionality](htt ## [Inter-Module Signals](https://opentitan.org/book/doc/contributing/hw/comportability/index.html#inter-signal-handling) -| Port Name | Package::Struct | Type | Act | Width | Description | -|:------------|:------------------|:--------|:------|--------:|:--------------| -| tl | tlul_pkg::tl | req_rsp | rsp | 1 | | +| Port Name | Package::Struct | Type | Act | Width | Description | +|:---------------|:------------------------------|:--------|:------|--------:|:-------------------------------------------------------------------------------------------------------------------------------------| +| racl_policies | top_racl_pkg::racl_policy_vec | uni | rcv | 1 | Incoming RACL policy vector from a racl_ctrl instance. The policy selection vector (parameter) selects the policy for each register. | +| racl_error | logic | uni | req | 1 | RACL error indication signal. If 1, the error log contains valid information. | +| racl_error_log | top_racl_pkg::racl_error_log | uni | req | 1 | RACL error log information of this module. | +| tl | tlul_pkg::tl | req_rsp | rsp | 1 | | ## Security Alerts diff --git a/hw/ip/pwm/pwm.core b/hw/ip/pwm/pwm.core index 3d18758715b80..d770e4dfadc5f 100644 --- a/hw/ip/pwm/pwm.core +++ b/hw/ip/pwm/pwm.core @@ -10,6 +10,7 @@ filesets: - lowrisc:prim:assert - lowrisc:ip:tlul - lowrisc:prim:all + - lowrisc:systems:top_racl_pkg files: - rtl/pwm_reg_pkg.sv - rtl/pwm_reg_top.sv diff --git a/hw/ip/pwm/rtl/pwm.sv b/hw/ip/pwm/rtl/pwm.sv index 21925e99a77c9..3979c7fbd47ec 100644 --- a/hw/ip/pwm/rtl/pwm.sv +++ b/hw/ip/pwm/rtl/pwm.sv @@ -7,9 +7,12 @@ module pwm import pwm_reg_pkg::*; #( - parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, - parameter int PhaseCntDw = 16, - parameter int BeatCntDw = 27 + parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 1'b1, + parameter int unsigned RaclPolicySelVec[23] = '{23{0}}, + parameter int PhaseCntDw = 16, + parameter int BeatCntDw = 27 ) ( input clk_i, input rst_ni, @@ -23,6 +26,11 @@ module pwm input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, + output logic [NOutputs-1:0] cio_pwm_o, output logic [NOutputs-1:0] cio_pwm_en_o ); @@ -30,16 +38,23 @@ module pwm pwm_reg_pkg::pwm_reg2hw_t reg2hw; logic [NumAlerts-1:0] alert_test, alerts; - pwm_reg_top u_reg ( + pwm_reg_top #( + .EnableRacl(EnableRacl), + .RaclErrorRsp(RaclErrorRsp), + .RaclPolicySelVec(RaclPolicySelVec) + ) u_reg ( .clk_i, .rst_ni, .clk_core_i, .rst_core_ni, - .tl_i (tl_i), - .tl_o (tl_o), - .reg2hw (reg2hw), + .tl_i (tl_i), + .tl_o (tl_o), + .reg2hw (reg2hw), + .racl_policies_i (racl_policies_i), + .racl_error_o (racl_error_o), + .racl_error_log_o (racl_error_log_o), // SEC_CM: BUS.INTEGRITY - .intg_err_o (alerts[0]) + .intg_err_o (alerts[0]) ); assign alert_test = { diff --git a/hw/ip/pwm/rtl/pwm_reg_top.sv b/hw/ip/pwm/rtl/pwm_reg_top.sv index 1112f581ad806..9ee9bd6e21ee9 100644 --- a/hw/ip/pwm/rtl/pwm_reg_top.sv +++ b/hw/ip/pwm/rtl/pwm_reg_top.sv @@ -6,7 +6,12 @@ `include "prim_assert.sv" -module pwm_reg_top ( +module pwm_reg_top + # ( + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 1'b1, + parameter int unsigned RaclPolicySelVec[23] = '{23{0}} + ) ( input clk_i, input rst_ni, input clk_core_i, @@ -16,6 +21,11 @@ module pwm_reg_top ( // To HW output pwm_reg_pkg::pwm_reg2hw_t reg2hw, // Write + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, + // Integrity check errors output logic intg_err_o ); @@ -111,7 +121,8 @@ module pwm_reg_top ( .be_o (reg_be), .busy_i (reg_busy), .rdata_i (reg_rdata), - .error_i (reg_error) + // Translate RACL error to TLUL error if enabled + .error_i (reg_error | (RaclErrorRsp & racl_error_o)) ); // cdc oversampling signals @@ -3087,8 +3098,32 @@ module pwm_reg_top ( logic [22:0] addr_hit; + top_racl_pkg::racl_role_vec_t racl_role_vec; + top_racl_pkg::racl_role_t racl_role; + + logic [22:0] racl_addr_hit_read; + logic [22:0] racl_addr_hit_write; + + if (EnableRacl) begin : gen_racl_role_logic + // Retrieve RACL role from user bits and one-hot encode that for the comparison bitmap + assign racl_role = top_racl_pkg::tlul_extract_racl_role_bits(tl_i.a_user.rsvd); + + prim_onehot_enc #( + .OneHotWidth( $bits(top_racl_pkg::racl_role_vec_t) ) + ) u_racl_role_encode ( + .in_i ( racl_role ), + .en_i ( 1'b1 ), + .out_o( racl_role_vec ) + ); + end else begin : gen_no_racl_role_logic + assign racl_role = '0; + assign racl_role_vec = '0; + end + always_comb begin addr_hit = '0; + racl_addr_hit_read = '0; + racl_addr_hit_write = '0; addr_hit[ 0] = (reg_addr == PWM_ALERT_TEST_OFFSET); addr_hit[ 1] = (reg_addr == PWM_REGWEN_OFFSET); addr_hit[ 2] = (reg_addr == PWM_CFG_OFFSET); @@ -3112,121 +3147,146 @@ module pwm_reg_top ( addr_hit[20] = (reg_addr == PWM_BLINK_PARAM_3_OFFSET); addr_hit[21] = (reg_addr == PWM_BLINK_PARAM_4_OFFSET); addr_hit[22] = (reg_addr == PWM_BLINK_PARAM_5_OFFSET); + + if (EnableRacl) begin : gen_racl_hit + for (int unsigned slice_idx = 0; slice_idx < 23; slice_idx++) begin + racl_addr_hit_read[slice_idx] = + addr_hit[slice_idx] & (|(racl_policies_i[RaclPolicySelVec[slice_idx]].read_perm + & racl_role_vec)); + racl_addr_hit_write[slice_idx] = + addr_hit[slice_idx] & (|(racl_policies_i[RaclPolicySelVec[slice_idx]].write_perm + & racl_role_vec)); + end + end else begin : gen_no_racl + racl_addr_hit_read = addr_hit; + racl_addr_hit_write = addr_hit; + end end assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + // Address hit but failed the RACL check + assign racl_error_o = (|addr_hit) & ~(|(addr_hit & (racl_addr_hit_read | racl_addr_hit_write))); + assign racl_error_log_o.racl_role = racl_role; + + if (EnableRacl) begin : gen_racl_log + assign racl_error_log_o.ctn_uid = top_racl_pkg::tlul_extract_ctn_uid_bits(tl_i.a_user.rsvd); + assign racl_error_log_o.read_access = tl_i.a_opcode == tlul_pkg::Get; + end else begin : gen_no_racl_log + assign racl_error_log_o.ctn_uid = '0; + assign racl_error_log_o.read_access = 1'b0; + end // Check sub-word write is permitted always_comb begin wr_err = (reg_we & - ((addr_hit[ 0] & (|(PWM_PERMIT[ 0] & ~reg_be))) | - (addr_hit[ 1] & (|(PWM_PERMIT[ 1] & ~reg_be))) | - (addr_hit[ 2] & (|(PWM_PERMIT[ 2] & ~reg_be))) | - (addr_hit[ 3] & (|(PWM_PERMIT[ 3] & ~reg_be))) | - (addr_hit[ 4] & (|(PWM_PERMIT[ 4] & ~reg_be))) | - (addr_hit[ 5] & (|(PWM_PERMIT[ 5] & ~reg_be))) | - (addr_hit[ 6] & (|(PWM_PERMIT[ 6] & ~reg_be))) | - (addr_hit[ 7] & (|(PWM_PERMIT[ 7] & ~reg_be))) | - (addr_hit[ 8] & (|(PWM_PERMIT[ 8] & ~reg_be))) | - (addr_hit[ 9] & (|(PWM_PERMIT[ 9] & ~reg_be))) | - (addr_hit[10] & (|(PWM_PERMIT[10] & ~reg_be))) | - (addr_hit[11] & (|(PWM_PERMIT[11] & ~reg_be))) | - (addr_hit[12] & (|(PWM_PERMIT[12] & ~reg_be))) | - (addr_hit[13] & (|(PWM_PERMIT[13] & ~reg_be))) | - (addr_hit[14] & (|(PWM_PERMIT[14] & ~reg_be))) | - (addr_hit[15] & (|(PWM_PERMIT[15] & ~reg_be))) | - (addr_hit[16] & (|(PWM_PERMIT[16] & ~reg_be))) | - (addr_hit[17] & (|(PWM_PERMIT[17] & ~reg_be))) | - (addr_hit[18] & (|(PWM_PERMIT[18] & ~reg_be))) | - (addr_hit[19] & (|(PWM_PERMIT[19] & ~reg_be))) | - (addr_hit[20] & (|(PWM_PERMIT[20] & ~reg_be))) | - (addr_hit[21] & (|(PWM_PERMIT[21] & ~reg_be))) | - (addr_hit[22] & (|(PWM_PERMIT[22] & ~reg_be))))); + ((racl_addr_hit_write[ 0] & (|(PWM_PERMIT[ 0] & ~reg_be))) | + (racl_addr_hit_write[ 1] & (|(PWM_PERMIT[ 1] & ~reg_be))) | + (racl_addr_hit_write[ 2] & (|(PWM_PERMIT[ 2] & ~reg_be))) | + (racl_addr_hit_write[ 3] & (|(PWM_PERMIT[ 3] & ~reg_be))) | + (racl_addr_hit_write[ 4] & (|(PWM_PERMIT[ 4] & ~reg_be))) | + (racl_addr_hit_write[ 5] & (|(PWM_PERMIT[ 5] & ~reg_be))) | + (racl_addr_hit_write[ 6] & (|(PWM_PERMIT[ 6] & ~reg_be))) | + (racl_addr_hit_write[ 7] & (|(PWM_PERMIT[ 7] & ~reg_be))) | + (racl_addr_hit_write[ 8] & (|(PWM_PERMIT[ 8] & ~reg_be))) | + (racl_addr_hit_write[ 9] & (|(PWM_PERMIT[ 9] & ~reg_be))) | + (racl_addr_hit_write[10] & (|(PWM_PERMIT[10] & ~reg_be))) | + (racl_addr_hit_write[11] & (|(PWM_PERMIT[11] & ~reg_be))) | + (racl_addr_hit_write[12] & (|(PWM_PERMIT[12] & ~reg_be))) | + (racl_addr_hit_write[13] & (|(PWM_PERMIT[13] & ~reg_be))) | + (racl_addr_hit_write[14] & (|(PWM_PERMIT[14] & ~reg_be))) | + (racl_addr_hit_write[15] & (|(PWM_PERMIT[15] & ~reg_be))) | + (racl_addr_hit_write[16] & (|(PWM_PERMIT[16] & ~reg_be))) | + (racl_addr_hit_write[17] & (|(PWM_PERMIT[17] & ~reg_be))) | + (racl_addr_hit_write[18] & (|(PWM_PERMIT[18] & ~reg_be))) | + (racl_addr_hit_write[19] & (|(PWM_PERMIT[19] & ~reg_be))) | + (racl_addr_hit_write[20] & (|(PWM_PERMIT[20] & ~reg_be))) | + (racl_addr_hit_write[21] & (|(PWM_PERMIT[21] & ~reg_be))) | + (racl_addr_hit_write[22] & (|(PWM_PERMIT[22] & ~reg_be))))); end // Generate write-enables - assign alert_test_we = addr_hit[0] & reg_we & !reg_error; + assign alert_test_we = racl_addr_hit_write[0] & reg_we & !reg_error; assign alert_test_wd = reg_wdata[0]; - assign regwen_we = addr_hit[1] & reg_we & !reg_error; + assign regwen_we = racl_addr_hit_write[1] & reg_we & !reg_error; assign regwen_wd = reg_wdata[0]; - assign cfg_we = addr_hit[2] & reg_we & !reg_error; + assign cfg_we = racl_addr_hit_write[2] & reg_we & !reg_error; - assign pwm_en_we = addr_hit[3] & reg_we & !reg_error; + assign pwm_en_we = racl_addr_hit_write[3] & reg_we & !reg_error; - assign invert_we = addr_hit[4] & reg_we & !reg_error; + assign invert_we = racl_addr_hit_write[4] & reg_we & !reg_error; - assign pwm_param_0_we = addr_hit[5] & reg_we & !reg_error; + assign pwm_param_0_we = racl_addr_hit_write[5] & reg_we & !reg_error; - assign pwm_param_1_we = addr_hit[6] & reg_we & !reg_error; + assign pwm_param_1_we = racl_addr_hit_write[6] & reg_we & !reg_error; - assign pwm_param_2_we = addr_hit[7] & reg_we & !reg_error; + assign pwm_param_2_we = racl_addr_hit_write[7] & reg_we & !reg_error; - assign pwm_param_3_we = addr_hit[8] & reg_we & !reg_error; + assign pwm_param_3_we = racl_addr_hit_write[8] & reg_we & !reg_error; - assign pwm_param_4_we = addr_hit[9] & reg_we & !reg_error; + assign pwm_param_4_we = racl_addr_hit_write[9] & reg_we & !reg_error; - assign pwm_param_5_we = addr_hit[10] & reg_we & !reg_error; + assign pwm_param_5_we = racl_addr_hit_write[10] & reg_we & !reg_error; - assign duty_cycle_0_we = addr_hit[11] & reg_we & !reg_error; + assign duty_cycle_0_we = racl_addr_hit_write[11] & reg_we & !reg_error; - assign duty_cycle_1_we = addr_hit[12] & reg_we & !reg_error; + assign duty_cycle_1_we = racl_addr_hit_write[12] & reg_we & !reg_error; - assign duty_cycle_2_we = addr_hit[13] & reg_we & !reg_error; + assign duty_cycle_2_we = racl_addr_hit_write[13] & reg_we & !reg_error; - assign duty_cycle_3_we = addr_hit[14] & reg_we & !reg_error; + assign duty_cycle_3_we = racl_addr_hit_write[14] & reg_we & !reg_error; - assign duty_cycle_4_we = addr_hit[15] & reg_we & !reg_error; + assign duty_cycle_4_we = racl_addr_hit_write[15] & reg_we & !reg_error; - assign duty_cycle_5_we = addr_hit[16] & reg_we & !reg_error; + assign duty_cycle_5_we = racl_addr_hit_write[16] & reg_we & !reg_error; - assign blink_param_0_we = addr_hit[17] & reg_we & !reg_error; + assign blink_param_0_we = racl_addr_hit_write[17] & reg_we & !reg_error; - assign blink_param_1_we = addr_hit[18] & reg_we & !reg_error; + assign blink_param_1_we = racl_addr_hit_write[18] & reg_we & !reg_error; - assign blink_param_2_we = addr_hit[19] & reg_we & !reg_error; + assign blink_param_2_we = racl_addr_hit_write[19] & reg_we & !reg_error; - assign blink_param_3_we = addr_hit[20] & reg_we & !reg_error; + assign blink_param_3_we = racl_addr_hit_write[20] & reg_we & !reg_error; - assign blink_param_4_we = addr_hit[21] & reg_we & !reg_error; + assign blink_param_4_we = racl_addr_hit_write[21] & reg_we & !reg_error; - assign blink_param_5_we = addr_hit[22] & reg_we & !reg_error; + assign blink_param_5_we = racl_addr_hit_write[22] & reg_we & !reg_error; @@ -3262,75 +3322,75 @@ module pwm_reg_top ( always_comb begin reg_rdata_next = '0; unique case (1'b1) - addr_hit[0]: begin + racl_addr_hit_read[0]: begin reg_rdata_next[0] = '0; end - addr_hit[1]: begin + racl_addr_hit_read[1]: begin reg_rdata_next[0] = regwen_qs; end - addr_hit[2]: begin + racl_addr_hit_read[2]: begin reg_rdata_next = DW'(cfg_qs); end - addr_hit[3]: begin + racl_addr_hit_read[3]: begin reg_rdata_next = DW'(pwm_en_qs); end - addr_hit[4]: begin + racl_addr_hit_read[4]: begin reg_rdata_next = DW'(invert_qs); end - addr_hit[5]: begin + racl_addr_hit_read[5]: begin reg_rdata_next = DW'(pwm_param_0_qs); end - addr_hit[6]: begin + racl_addr_hit_read[6]: begin reg_rdata_next = DW'(pwm_param_1_qs); end - addr_hit[7]: begin + racl_addr_hit_read[7]: begin reg_rdata_next = DW'(pwm_param_2_qs); end - addr_hit[8]: begin + racl_addr_hit_read[8]: begin reg_rdata_next = DW'(pwm_param_3_qs); end - addr_hit[9]: begin + racl_addr_hit_read[9]: begin reg_rdata_next = DW'(pwm_param_4_qs); end - addr_hit[10]: begin + racl_addr_hit_read[10]: begin reg_rdata_next = DW'(pwm_param_5_qs); end - addr_hit[11]: begin + racl_addr_hit_read[11]: begin reg_rdata_next = DW'(duty_cycle_0_qs); end - addr_hit[12]: begin + racl_addr_hit_read[12]: begin reg_rdata_next = DW'(duty_cycle_1_qs); end - addr_hit[13]: begin + racl_addr_hit_read[13]: begin reg_rdata_next = DW'(duty_cycle_2_qs); end - addr_hit[14]: begin + racl_addr_hit_read[14]: begin reg_rdata_next = DW'(duty_cycle_3_qs); end - addr_hit[15]: begin + racl_addr_hit_read[15]: begin reg_rdata_next = DW'(duty_cycle_4_qs); end - addr_hit[16]: begin + racl_addr_hit_read[16]: begin reg_rdata_next = DW'(duty_cycle_5_qs); end - addr_hit[17]: begin + racl_addr_hit_read[17]: begin reg_rdata_next = DW'(blink_param_0_qs); end - addr_hit[18]: begin + racl_addr_hit_read[18]: begin reg_rdata_next = DW'(blink_param_1_qs); end - addr_hit[19]: begin + racl_addr_hit_read[19]: begin reg_rdata_next = DW'(blink_param_2_qs); end - addr_hit[20]: begin + racl_addr_hit_read[20]: begin reg_rdata_next = DW'(blink_param_3_qs); end - addr_hit[21]: begin + racl_addr_hit_read[21]: begin reg_rdata_next = DW'(blink_param_4_qs); end - addr_hit[22]: begin + racl_addr_hit_read[22]: begin reg_rdata_next = DW'(blink_param_5_qs); end default: begin @@ -3427,6 +3487,8 @@ module pwm_reg_top ( logic unused_be; assign unused_wdata = ^reg_wdata; assign unused_be = ^reg_be; + logic unused_policy_sel; + assign unused_policy_sel = ^racl_policies_i; // Assertions for Register Interface `ASSERT_PULSE(wePulse, reg_we, clk_i, !rst_ni) diff --git a/hw/ip/tlul/adapter_reg_racl.core b/hw/ip/tlul/adapter_reg_racl.core new file mode 100644 index 0000000000000..80e75dbb7714a --- /dev/null +++ b/hw/ip/tlul/adapter_reg_racl.core @@ -0,0 +1,59 @@ +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: "lowrisc:tlul:adapter_reg_racl:0.1" +description: "TL-UL to Register interface adapter with RACL protection" + +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:secded + - lowrisc:tlul:common + - lowrisc:tlul:trans_intg + - lowrisc:systems:top_racl_pkg + files: + - rtl/tlul_adapter_reg_racl.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +parameters: + SYNTHESIS: + datatype: bool + paramtype: vlogdefine + + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + toplevel: tlul_adapter_reg_racl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/hw/ip/tlul/rtl/tlul_adapter_reg_racl.sv b/hw/ip/tlul/rtl/tlul_adapter_reg_racl.sv new file mode 100644 index 0000000000000..4639409e5cf02 --- /dev/null +++ b/hw/ip/tlul/rtl/tlul_adapter_reg_racl.sv @@ -0,0 +1,123 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +/** + * Tile-Link UL adapter for Register interface with RACL protection + */ + +module tlul_adapter_reg_racl + import tlul_pkg::*; + import prim_mubi_pkg::mubi4_t; +#( + parameter bit CmdIntgCheck = 0, // 1: Enable command integrity check + parameter bit EnableRspIntgGen = 0, // 1: Generate response integrity + parameter bit EnableDataIntgGen = 0, // 1: Generate response data integrity + parameter int RegAw = 8, // Width of register address + parameter int RegDw = 32, // Shall be matched with TL_DW + parameter int AccessLatency = 0, // 0: same cycle, 1: next cycle + parameter bit EnableRacl = 0, // 1: Enable RACL checks on access + parameter bit RaclErrorRsp = 1, // 1: Return TLUL error on RACL errors + parameter int RaclPolicySelVec = 0, // RACL policy for this reg adapter + localparam int RegBw = RegDw/8 +) ( + input clk_i, + input rst_ni, + + // TL-UL interface + input tl_h2d_t tl_i, + output tl_d2h_t tl_o, + + // control interface + input mubi4_t en_ifetch_i, + output logic intg_error_o, + + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, + + // Register interface + output logic re_o, + output logic we_o, + output logic [RegAw-1:0] addr_o, + output logic [RegDw-1:0] wdata_o, + output logic [RegBw-1:0] be_o, + input busy_i, + // The following two signals are expected + // to be returned in AccessLatency cycles. + input [RegDw-1:0] rdata_i, + // This can be a write or read error. + input error_i +); + logic racl_read_allowed, racl_write_allowed, racl_error; + logic rd_req, wr_req; + logic [RegDw-1:0] rdata; + + tlul_adapter_reg #( + .CmdIntgCheck (CmdIntgCheck), + .EnableRspIntgGen (EnableRspIntgGen), + .EnableDataIntgGen (EnableDataIntgGen), + .RegAw (RegAw), + .RegDw (RegDw), + .AccessLatency (AccessLatency) + ) tlul_adapter_reg ( + .clk_i, + .rst_ni, + .tl_i, + .tl_o, + .en_ifetch_i, + .intg_error_o, + .re_o(rd_req), + .we_o(wr_req), + .addr_o, + .wdata_o, + .be_o, + .busy_i, + .rdata_i(rdata), + .error_i(racl_error) + ); + + if (EnableRacl) begin : gen_racl_role_logic + // Retrieve RACL role from user bits and one-hot encode that for the comparison bitmap + top_racl_pkg::racl_role_t racl_role; + assign racl_role = top_racl_pkg::tlul_extract_racl_role_bits(tl_i.a_user.rsvd); + + top_racl_pkg::racl_role_vec_t racl_role_vec; + prim_onehot_enc #( + .OneHotWidth( $bits(top_racl_pkg::racl_role_vec_t) ) + ) u_racl_role_encode ( + .in_i ( racl_role ), + .en_i ( 1'b1 ), + .out_o( racl_role_vec ) + ); + + assign racl_read_allowed = (|(racl_policies_i[RaclPolicySelVec].read_perm & racl_role_vec)); + assign racl_write_allowed = (|(racl_policies_i[RaclPolicySelVec].write_perm & racl_role_vec)); + assign racl_error_o = (rd_req & ~racl_read_allowed) | (wr_req & ~racl_write_allowed); + // RACL only generates error responeses if enabled + assign racl_error = racl_error_o & RaclErrorRsp; + // Collect RACL error information + assign racl_error_log_o.read_access = tl_i.a_opcode == tlul_pkg::Get; + assign racl_error_log_o.racl_role = racl_role; + assign racl_error_log_o.ctn_uid = top_racl_pkg::tlul_extract_ctn_uid_bits(tl_i.a_user.rsvd); + end else begin : gen_no_racl_role_logic + assign racl_read_allowed = 1'b1; + assign racl_write_allowed = 1'b1; + assign racl_error = 1'b0; + assign racl_error_o = 1'b0; + assign racl_error_log_o = '0; + end + + // Not all RACL policies are used, even if RACL is enabled + logic unused_policy_sel; + assign unused_policy_sel = ^racl_policies_i; + + assign we_o = wr_req & racl_write_allowed; + assign re_o = rd_req & racl_read_allowed; + // Mask read data in case of a RACL violation + assign rdata = racl_error_o ? '1 : rdata_i; + +endmodule diff --git a/hw/ip/tlul/tlul.core b/hw/ip/tlul/tlul.core index 434fa6a35f245..d6fb560fbf58d 100644 --- a/hw/ip/tlul/tlul.core +++ b/hw/ip/tlul/tlul.core @@ -12,6 +12,7 @@ filesets: - lowrisc:tlul:socket_m1 - lowrisc:tlul:adapter_sram - lowrisc:tlul:adapter_reg + - lowrisc:tlul:adapter_reg_racl - lowrisc:tlul:adapter_dmi - lowrisc:tlul:jtag_dtm - lowrisc:tlul:sram2tlul diff --git a/hw/ip/uart/data/uart.hjson b/hw/ip/uart/data/uart.hjson index 9e59adc973bda..a314f2eac599b 100644 --- a/hw/ip/uart/data/uart.hjson +++ b/hw/ip/uart/data/uart.hjson @@ -48,7 +48,7 @@ ] clocking: [{clock: "clk_i", reset: "rst_ni"}], bus_interfaces: [ - { protocol: "tlul", direction: "device" } + { protocol: "tlul", direction: "device", racl_support: true } ], available_input_list: [ { name: "rx", desc: "Serial receive bit" } @@ -138,6 +138,36 @@ ''' act: "req" } + { struct: "racl_policy_vec", + type: "uni", + name: "racl_policies", + act: "rcv", + package: "top_racl_pkg", + desc: ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + } + { struct: "logic", + type: "uni", + name: "racl_error", + act: "req", + width : "1", + desc: ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + } + { struct: "racl_error_log", + type: "uni", + name: "racl_error_log", + act: "req", + width: "1" + package: "top_racl_pkg", + desc: ''' + RACL error log information of this module. + ''' + } ] countermeasures: [ { name: "BUS.INTEGRITY", diff --git a/hw/ip/uart/doc/interfaces.md b/hw/ip/uart/doc/interfaces.md index 102fec3f2ca34..018437c0815f8 100644 --- a/hw/ip/uart/doc/interfaces.md +++ b/hw/ip/uart/doc/interfaces.md @@ -16,10 +16,13 @@ Referring to the [Comportable guideline for peripheral device functionality](htt ## [Inter-Module Signals](https://opentitan.org/book/doc/contributing/hw/comportability/index.html#inter-signal-handling) -| Port Name | Package::Struct | Type | Act | Width | Description | -|:-------------|:------------------|:--------|:------|--------:|:-----------------------------------------------------------------------------------------------------------------------------------------------| -| lsio_trigger | logic | uni | req | 1 | Self-clearing status trigger for the DMA. Set when RX or TX FIFOs are past their configured watermarks matching watermark interrupt behaviour. | -| tl | tlul_pkg::tl | req_rsp | rsp | 1 | | +| Port Name | Package::Struct | Type | Act | Width | Description | +|:---------------|:------------------------------|:--------|:------|--------:|:-----------------------------------------------------------------------------------------------------------------------------------------------| +| lsio_trigger | logic | uni | req | 1 | Self-clearing status trigger for the DMA. Set when RX or TX FIFOs are past their configured watermarks matching watermark interrupt behaviour. | +| racl_policies | top_racl_pkg::racl_policy_vec | uni | rcv | 1 | Incoming RACL policy vector from a racl_ctrl instance. The policy selection vector (parameter) selects the policy for each register. | +| racl_error | logic | uni | req | 1 | RACL error indication signal. If 1, the error log contains valid information. | +| racl_error_log | top_racl_pkg::racl_error_log | uni | req | 1 | RACL error log information of this module. | +| tl | tlul_pkg::tl | req_rsp | rsp | 1 | | ## Interrupts diff --git a/hw/ip/uart/rtl/uart.sv b/hw/ip/uart/rtl/uart.sv index 341f964321ec1..fcb2f708b3ccc 100644 --- a/hw/ip/uart/rtl/uart.sv +++ b/hw/ip/uart/rtl/uart.sv @@ -9,7 +9,10 @@ module uart import uart_reg_pkg::*; #( - parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}} + parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 1'b1, + parameter int unsigned RaclPolicySelVec[13] = '{13{0}} ) ( input clk_i, input rst_ni, @@ -22,6 +25,11 @@ module uart input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, + output logic lsio_trigger_o, // Generic IO @@ -45,13 +53,20 @@ module uart uart_reg2hw_t reg2hw; uart_hw2reg_t hw2reg; - uart_reg_top u_reg ( + uart_reg_top #( + .EnableRacl(EnableRacl), + .RaclErrorRsp(RaclErrorRsp), + .RaclPolicySelVec(RaclPolicySelVec) + ) u_reg ( .clk_i, .rst_ni, .tl_i, .tl_o, .reg2hw, .hw2reg, + .racl_policies_i, + .racl_error_o, + .racl_error_log_o, // SEC_CM: BUS.INTEGRITY .intg_err_o (alerts[0]) ); diff --git a/hw/ip/uart/rtl/uart_reg_top.sv b/hw/ip/uart/rtl/uart_reg_top.sv index 6648443875e62..c70029ed4de5c 100644 --- a/hw/ip/uart/rtl/uart_reg_top.sv +++ b/hw/ip/uart/rtl/uart_reg_top.sv @@ -6,7 +6,12 @@ `include "prim_assert.sv" -module uart_reg_top ( +module uart_reg_top + # ( + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 1'b1, + parameter int unsigned RaclPolicySelVec[13] = '{13{0}} + ) ( input clk_i, input rst_ni, input tlul_pkg::tl_h2d_t tl_i, @@ -15,6 +20,11 @@ module uart_reg_top ( output uart_reg_pkg::uart_reg2hw_t reg2hw, // Write input uart_reg_pkg::uart_hw2reg_t hw2reg, // Read + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, + // Integrity check errors output logic intg_err_o ); @@ -110,7 +120,8 @@ module uart_reg_top ( .be_o (reg_be), .busy_i (reg_busy), .rdata_i (reg_rdata), - .error_i (reg_error) + // Translate RACL error to TLUL error if enabled + .error_i (reg_error | (RaclErrorRsp & racl_error_o)) ); // cdc oversampling signals @@ -1561,8 +1572,32 @@ module uart_reg_top ( logic [12:0] addr_hit; + top_racl_pkg::racl_role_vec_t racl_role_vec; + top_racl_pkg::racl_role_t racl_role; + + logic [12:0] racl_addr_hit_read; + logic [12:0] racl_addr_hit_write; + + if (EnableRacl) begin : gen_racl_role_logic + // Retrieve RACL role from user bits and one-hot encode that for the comparison bitmap + assign racl_role = top_racl_pkg::tlul_extract_racl_role_bits(tl_i.a_user.rsvd); + + prim_onehot_enc #( + .OneHotWidth( $bits(top_racl_pkg::racl_role_vec_t) ) + ) u_racl_role_encode ( + .in_i ( racl_role ), + .en_i ( 1'b1 ), + .out_o( racl_role_vec ) + ); + end else begin : gen_no_racl_role_logic + assign racl_role = '0; + assign racl_role_vec = '0; + end + always_comb begin addr_hit = '0; + racl_addr_hit_read = '0; + racl_addr_hit_write = '0; addr_hit[ 0] = (reg_addr == UART_INTR_STATE_OFFSET); addr_hit[ 1] = (reg_addr == UART_INTR_ENABLE_OFFSET); addr_hit[ 2] = (reg_addr == UART_INTR_TEST_OFFSET); @@ -1576,30 +1611,55 @@ module uart_reg_top ( addr_hit[10] = (reg_addr == UART_OVRD_OFFSET); addr_hit[11] = (reg_addr == UART_VAL_OFFSET); addr_hit[12] = (reg_addr == UART_TIMEOUT_CTRL_OFFSET); + + if (EnableRacl) begin : gen_racl_hit + for (int unsigned slice_idx = 0; slice_idx < 13; slice_idx++) begin + racl_addr_hit_read[slice_idx] = + addr_hit[slice_idx] & (|(racl_policies_i[RaclPolicySelVec[slice_idx]].read_perm + & racl_role_vec)); + racl_addr_hit_write[slice_idx] = + addr_hit[slice_idx] & (|(racl_policies_i[RaclPolicySelVec[slice_idx]].write_perm + & racl_role_vec)); + end + end else begin : gen_no_racl + racl_addr_hit_read = addr_hit; + racl_addr_hit_write = addr_hit; + end end assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + // Address hit but failed the RACL check + assign racl_error_o = (|addr_hit) & ~(|(addr_hit & (racl_addr_hit_read | racl_addr_hit_write))); + assign racl_error_log_o.racl_role = racl_role; + + if (EnableRacl) begin : gen_racl_log + assign racl_error_log_o.ctn_uid = top_racl_pkg::tlul_extract_ctn_uid_bits(tl_i.a_user.rsvd); + assign racl_error_log_o.read_access = tl_i.a_opcode == tlul_pkg::Get; + end else begin : gen_no_racl_log + assign racl_error_log_o.ctn_uid = '0; + assign racl_error_log_o.read_access = 1'b0; + end // Check sub-word write is permitted always_comb begin wr_err = (reg_we & - ((addr_hit[ 0] & (|(UART_PERMIT[ 0] & ~reg_be))) | - (addr_hit[ 1] & (|(UART_PERMIT[ 1] & ~reg_be))) | - (addr_hit[ 2] & (|(UART_PERMIT[ 2] & ~reg_be))) | - (addr_hit[ 3] & (|(UART_PERMIT[ 3] & ~reg_be))) | - (addr_hit[ 4] & (|(UART_PERMIT[ 4] & ~reg_be))) | - (addr_hit[ 5] & (|(UART_PERMIT[ 5] & ~reg_be))) | - (addr_hit[ 6] & (|(UART_PERMIT[ 6] & ~reg_be))) | - (addr_hit[ 7] & (|(UART_PERMIT[ 7] & ~reg_be))) | - (addr_hit[ 8] & (|(UART_PERMIT[ 8] & ~reg_be))) | - (addr_hit[ 9] & (|(UART_PERMIT[ 9] & ~reg_be))) | - (addr_hit[10] & (|(UART_PERMIT[10] & ~reg_be))) | - (addr_hit[11] & (|(UART_PERMIT[11] & ~reg_be))) | - (addr_hit[12] & (|(UART_PERMIT[12] & ~reg_be))))); + ((racl_addr_hit_write[ 0] & (|(UART_PERMIT[ 0] & ~reg_be))) | + (racl_addr_hit_write[ 1] & (|(UART_PERMIT[ 1] & ~reg_be))) | + (racl_addr_hit_write[ 2] & (|(UART_PERMIT[ 2] & ~reg_be))) | + (racl_addr_hit_write[ 3] & (|(UART_PERMIT[ 3] & ~reg_be))) | + (racl_addr_hit_write[ 4] & (|(UART_PERMIT[ 4] & ~reg_be))) | + (racl_addr_hit_write[ 5] & (|(UART_PERMIT[ 5] & ~reg_be))) | + (racl_addr_hit_write[ 6] & (|(UART_PERMIT[ 6] & ~reg_be))) | + (racl_addr_hit_write[ 7] & (|(UART_PERMIT[ 7] & ~reg_be))) | + (racl_addr_hit_write[ 8] & (|(UART_PERMIT[ 8] & ~reg_be))) | + (racl_addr_hit_write[ 9] & (|(UART_PERMIT[ 9] & ~reg_be))) | + (racl_addr_hit_write[10] & (|(UART_PERMIT[10] & ~reg_be))) | + (racl_addr_hit_write[11] & (|(UART_PERMIT[11] & ~reg_be))) | + (racl_addr_hit_write[12] & (|(UART_PERMIT[12] & ~reg_be))))); end // Generate write-enables - assign intr_state_we = addr_hit[0] & reg_we & !reg_error; + assign intr_state_we = racl_addr_hit_write[0] & reg_we & !reg_error; assign intr_state_tx_done_wd = reg_wdata[2]; @@ -1612,7 +1672,7 @@ module uart_reg_top ( assign intr_state_rx_timeout_wd = reg_wdata[6]; assign intr_state_rx_parity_err_wd = reg_wdata[7]; - assign intr_enable_we = addr_hit[1] & reg_we & !reg_error; + assign intr_enable_we = racl_addr_hit_write[1] & reg_we & !reg_error; assign intr_enable_tx_watermark_wd = reg_wdata[0]; @@ -1631,7 +1691,7 @@ module uart_reg_top ( assign intr_enable_rx_parity_err_wd = reg_wdata[7]; assign intr_enable_tx_empty_wd = reg_wdata[8]; - assign intr_test_we = addr_hit[2] & reg_we & !reg_error; + assign intr_test_we = racl_addr_hit_write[2] & reg_we & !reg_error; assign intr_test_tx_watermark_wd = reg_wdata[0]; @@ -1650,10 +1710,10 @@ module uart_reg_top ( assign intr_test_rx_parity_err_wd = reg_wdata[7]; assign intr_test_tx_empty_wd = reg_wdata[8]; - assign alert_test_we = addr_hit[3] & reg_we & !reg_error; + assign alert_test_we = racl_addr_hit_write[3] & reg_we & !reg_error; assign alert_test_wd = reg_wdata[0]; - assign ctrl_we = addr_hit[4] & reg_we & !reg_error; + assign ctrl_we = racl_addr_hit_write[4] & reg_we & !reg_error; assign ctrl_tx_wd = reg_wdata[0]; @@ -1672,12 +1732,12 @@ module uart_reg_top ( assign ctrl_rxblvl_wd = reg_wdata[9:8]; assign ctrl_nco_wd = reg_wdata[31:16]; - assign status_re = addr_hit[5] & reg_re & !reg_error; - assign rdata_re = addr_hit[6] & reg_re & !reg_error; - assign wdata_we = addr_hit[7] & reg_we & !reg_error; + assign status_re = racl_addr_hit_write[5] & reg_re & !reg_error; + assign rdata_re = racl_addr_hit_write[6] & reg_re & !reg_error; + assign wdata_we = racl_addr_hit_write[7] & reg_we & !reg_error; assign wdata_wd = reg_wdata[7:0]; - assign fifo_ctrl_we = addr_hit[8] & reg_we & !reg_error; + assign fifo_ctrl_we = racl_addr_hit_write[8] & reg_we & !reg_error; assign fifo_ctrl_rxrst_wd = reg_wdata[0]; @@ -1686,14 +1746,14 @@ module uart_reg_top ( assign fifo_ctrl_rxilvl_wd = reg_wdata[4:2]; assign fifo_ctrl_txilvl_wd = reg_wdata[7:5]; - assign fifo_status_re = addr_hit[9] & reg_re & !reg_error; - assign ovrd_we = addr_hit[10] & reg_we & !reg_error; + assign fifo_status_re = racl_addr_hit_write[9] & reg_re & !reg_error; + assign ovrd_we = racl_addr_hit_write[10] & reg_we & !reg_error; assign ovrd_txen_wd = reg_wdata[0]; assign ovrd_txval_wd = reg_wdata[1]; - assign val_re = addr_hit[11] & reg_re & !reg_error; - assign timeout_ctrl_we = addr_hit[12] & reg_we & !reg_error; + assign val_re = racl_addr_hit_write[11] & reg_re & !reg_error; + assign timeout_ctrl_we = racl_addr_hit_write[12] & reg_we & !reg_error; assign timeout_ctrl_val_wd = reg_wdata[23:0]; @@ -1721,7 +1781,7 @@ module uart_reg_top ( always_comb begin reg_rdata_next = '0; unique case (1'b1) - addr_hit[0]: begin + racl_addr_hit_read[0]: begin reg_rdata_next[0] = intr_state_tx_watermark_qs; reg_rdata_next[1] = intr_state_rx_watermark_qs; reg_rdata_next[2] = intr_state_tx_done_qs; @@ -1733,7 +1793,7 @@ module uart_reg_top ( reg_rdata_next[8] = intr_state_tx_empty_qs; end - addr_hit[1]: begin + racl_addr_hit_read[1]: begin reg_rdata_next[0] = intr_enable_tx_watermark_qs; reg_rdata_next[1] = intr_enable_rx_watermark_qs; reg_rdata_next[2] = intr_enable_tx_done_qs; @@ -1745,7 +1805,7 @@ module uart_reg_top ( reg_rdata_next[8] = intr_enable_tx_empty_qs; end - addr_hit[2]: begin + racl_addr_hit_read[2]: begin reg_rdata_next[0] = '0; reg_rdata_next[1] = '0; reg_rdata_next[2] = '0; @@ -1757,11 +1817,11 @@ module uart_reg_top ( reg_rdata_next[8] = '0; end - addr_hit[3]: begin + racl_addr_hit_read[3]: begin reg_rdata_next[0] = '0; end - addr_hit[4]: begin + racl_addr_hit_read[4]: begin reg_rdata_next[0] = ctrl_tx_qs; reg_rdata_next[1] = ctrl_rx_qs; reg_rdata_next[2] = ctrl_nf_qs; @@ -1773,7 +1833,7 @@ module uart_reg_top ( reg_rdata_next[31:16] = ctrl_nco_qs; end - addr_hit[5]: begin + racl_addr_hit_read[5]: begin reg_rdata_next[0] = status_txfull_qs; reg_rdata_next[1] = status_rxfull_qs; reg_rdata_next[2] = status_txempty_qs; @@ -1782,36 +1842,36 @@ module uart_reg_top ( reg_rdata_next[5] = status_rxempty_qs; end - addr_hit[6]: begin + racl_addr_hit_read[6]: begin reg_rdata_next[7:0] = rdata_qs; end - addr_hit[7]: begin + racl_addr_hit_read[7]: begin reg_rdata_next[7:0] = '0; end - addr_hit[8]: begin + racl_addr_hit_read[8]: begin reg_rdata_next[0] = '0; reg_rdata_next[1] = '0; reg_rdata_next[4:2] = fifo_ctrl_rxilvl_qs; reg_rdata_next[7:5] = fifo_ctrl_txilvl_qs; end - addr_hit[9]: begin + racl_addr_hit_read[9]: begin reg_rdata_next[7:0] = fifo_status_txlvl_qs; reg_rdata_next[23:16] = fifo_status_rxlvl_qs; end - addr_hit[10]: begin + racl_addr_hit_read[10]: begin reg_rdata_next[0] = ovrd_txen_qs; reg_rdata_next[1] = ovrd_txval_qs; end - addr_hit[11]: begin + racl_addr_hit_read[11]: begin reg_rdata_next[15:0] = val_qs; end - addr_hit[12]: begin + racl_addr_hit_read[12]: begin reg_rdata_next[23:0] = timeout_ctrl_val_qs; reg_rdata_next[31] = timeout_ctrl_en_qs; end @@ -1837,6 +1897,8 @@ module uart_reg_top ( logic unused_be; assign unused_wdata = ^reg_wdata; assign unused_be = ^reg_be; + logic unused_policy_sel; + assign unused_policy_sel = ^racl_policies_i; // Assertions for Register Interface `ASSERT_PULSE(wePulse, reg_we, clk_i, !rst_ni) diff --git a/hw/ip/uart/uart.core b/hw/ip/uart/uart.core index 6f6c0773ce683..6f7c494fdc9b2 100644 --- a/hw/ip/uart/uart.core +++ b/hw/ip/uart/uart.core @@ -10,6 +10,7 @@ filesets: - lowrisc:constants:top_pkg - lowrisc:prim:all - lowrisc:ip:tlul + - lowrisc:systems:top_racl_pkg files: - rtl/uart_reg_pkg.sv - rtl/uart_reg_top.sv diff --git a/hw/ip_templates/ac_range_check/data/ac_range_check.hjson.tpl b/hw/ip_templates/ac_range_check/data/ac_range_check.hjson.tpl index 2ad71d9c45132..c701441548252 100644 --- a/hw/ip_templates/ac_range_check/data/ac_range_check.hjson.tpl +++ b/hw/ip_templates/ac_range_check/data/ac_range_check.hjson.tpl @@ -19,7 +19,7 @@ import math clocking: [{clock: "clk_i", reset: "rst_ni", primary: true}] bus_interfaces: [ - { protocol: "tlul", direction: "device", hier_path: "u_ac_range_check_reg" } + { protocol: "tlul", direction: "device", hier_path: "u_ac_range_check_reg", racl_support: true } ] param_list: [ { name: "NumRanges", diff --git a/hw/ip_templates/ac_range_check/rtl/ac_range_check.sv.tpl b/hw/ip_templates/ac_range_check/rtl/ac_range_check.sv.tpl index 890a47bd00551..e36c274ca9b19 100644 --- a/hw/ip_templates/ac_range_check/rtl/ac_range_check.sv.tpl +++ b/hw/ip_templates/ac_range_check/rtl/ac_range_check.sv.tpl @@ -6,7 +6,9 @@ module ${module_instance_name} import tlul_pkg::*; import ${module_instance_name}_reg_pkg::*; #( - parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}} + parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + parameter bit RaclErrorRsp = 1'b1, + parameter int unsigned RaclPolicySelVec[${3 + 5*num_ranges}] = '{${3 + 5*num_ranges}{0}} ) ( input logic clk_i, input logic rst_ni, @@ -14,6 +16,10 @@ module ${module_instance_name} // Alerts input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, // Access range check interrupts output logic intr_deny_cnt_reached_o, // Inter module signals @@ -33,7 +39,11 @@ module ${module_instance_name} ////////////////////////////////////////////////////////////////////////////// logic reg_intg_error, shadowed_storage_err, shadowed_update_err; // SEC_CM: BUS.INTEGRITY - ${module_instance_name}_reg_top u_ac_range_check_reg ( + ${module_instance_name}_reg_top #( + .EnableRacl(EnableRacl), + .RaclErrorRsp(RaclErrorRsp), + .RaclPolicySelVec(RaclPolicySelVec) + ) u_ac_range_check_reg ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .rst_shadowed_ni ( rst_shadowed_ni ), @@ -41,6 +51,9 @@ module ${module_instance_name} .tl_o ( tl_o ), .reg2hw ( reg2hw ), .hw2reg ( hw2reg ), + .racl_policies_i ( racl_policies_i ), + .racl_error_o ( racl_error_o ), + .racl_error_log_o ( racl_error_log_o ), .shadowed_storage_err_o ( shadowed_storage_err ), .shadowed_update_err_o ( shadowed_update_err ), .intg_err_o ( reg_intg_error ) diff --git a/hw/ip_templates/alert_handler/alert_handler_reg.core.tpl b/hw/ip_templates/alert_handler/alert_handler_reg.core.tpl index e0574eb8e2711..83383fc853882 100644 --- a/hw/ip_templates/alert_handler/alert_handler_reg.core.tpl +++ b/hw/ip_templates/alert_handler/alert_handler_reg.core.tpl @@ -12,6 +12,9 @@ filesets: - lowrisc:ip:tlul - lowrisc:prim:subreg - ${instance_vlnv(f"lowrisc:ip:{module_instance_name}_pkg")} + % if racl_support: + - lowrisc:systems:top_racl_pkg + % endif files: - rtl/${module_instance_name}_reg_top.sv file_type: systemVerilogSource diff --git a/hw/ip_templates/alert_handler/data/alert_handler.hjson.tpl b/hw/ip_templates/alert_handler/data/alert_handler.hjson.tpl index db7772ed465a4..bd638c394b63a 100644 --- a/hw/ip_templates/alert_handler/data/alert_handler.hjson.tpl +++ b/hw/ip_templates/alert_handler/data/alert_handler.hjson.tpl @@ -26,7 +26,11 @@ chars = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] {clock: "clk_edn_i", reset: "rst_edn_ni"} ] bus_interfaces: [ + % if racl_support: + { protocol: "tlul", direction: "device", hier_path: "u_reg_wrap.u_reg", racl_support: true } + % else: { protocol: "tlul", direction: "device", hier_path: "u_reg_wrap.u_reg" } + % endif ], regwidth: "32", ############################################################################## @@ -236,6 +240,38 @@ chars = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] width: "4", // N_ESC_SEV package: "prim_esc_pkg" }, + % if racl_support: + { struct: "racl_policy_vec", + type: "uni", + name: "racl_policies", + act: "rcv", + package: "top_racl_pkg", + desc: ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + } + { struct: "logic", + type: "uni", + name: "racl_error", + act: "req", + width : "1", + desc: ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + } + { struct: "racl_error_log", + type: "uni", + name: "racl_error_log", + act: "req", + width: "1" + package: "top_racl_pkg", + desc: ''' + RACL error log information of this module. + ''' + } + % endif ] features: [ diff --git a/hw/ip_templates/alert_handler/data/alert_handler.tpldesc.hjson b/hw/ip_templates/alert_handler/data/alert_handler.tpldesc.hjson index cf278cb466f95..e308897990691 100644 --- a/hw/ip_templates/alert_handler/data/alert_handler.tpldesc.hjson +++ b/hw/ip_templates/alert_handler/data/alert_handler.tpldesc.hjson @@ -63,5 +63,11 @@ type: "string" default: "alert_handler" } + { + name: "racl_support" + desc: "Enable RACL support" + type: "bool" + default: false + } ] } diff --git a/hw/ip_templates/alert_handler/rtl/alert_handler.sv.tpl b/hw/ip_templates/alert_handler/rtl/alert_handler.sv.tpl index fa58b95b98442..0993e6f0f52b9 100644 --- a/hw/ip_templates/alert_handler/rtl/alert_handler.sv.tpl +++ b/hw/ip_templates/alert_handler/rtl/alert_handler.sv.tpl @@ -11,6 +11,14 @@ module ${module_instance_name} import prim_alert_pkg::*; import prim_esc_pkg::*; #( +% if racl_support: + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 1'b1, +<% +num_regs = 6 + 4 * n_alerts + 4 * 7 + n_classes * 14 +%>\ + parameter int unsigned RaclPolicySelVec[${num_regs}] = '{${num_regs}{0}}, +% endif // Compile time random constants, to be overriden by topgen. parameter lfsr_seed_t RndCnstLfsrSeed = RndCnstLfsrSeedDefault, parameter lfsr_perm_t RndCnstLfsrPerm = RndCnstLfsrPermDefault @@ -41,6 +49,12 @@ module ${module_instance_name} // SEC_CM: ALERT.INTERSIG.DIFF input prim_alert_pkg::alert_tx_t [NAlerts-1:0] alert_tx_i, output prim_alert_pkg::alert_rx_t [NAlerts-1:0] alert_rx_o, +% if racl_support: + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, +% endif // Escalation outputs // SEC_CM: ESC.INTERSIG.DIFF input prim_esc_pkg::esc_rx_t [N_ESC_SEV-1:0] esc_rx_i, @@ -67,7 +81,15 @@ module ${module_instance_name} // SEC_CM: ALERT.CONFIG.REGWEN // SEC_CM: ALERT_LOC.CONFIG.REGWEN // SEC_CM: CLASS.CONFIG.REGWEN +% if racl_support: + ${module_instance_name}_reg_wrap #( + .EnableRacl(EnableRacl), + .RaclErrorRsp(RaclErrorRsp), + .RaclPolicySelVec(RaclPolicySelVec) + ) u_reg_wrap ( +% else: ${module_instance_name}_reg_wrap u_reg_wrap ( +% endif .clk_i, .rst_ni, .rst_shadowed_ni, @@ -78,6 +100,11 @@ module ${module_instance_name} .crashdump_o, .hw2reg_wrap, .reg2hw_wrap, + % if racl_support: + .racl_policies_i, + .racl_error_o, + .racl_error_log_o, + % endif // SEC_CM: BUS.INTEGRITY .fatal_integ_alert_o(loc_alert_trig[4]) ); diff --git a/hw/ip_templates/alert_handler/rtl/alert_handler_reg_wrap.sv.tpl b/hw/ip_templates/alert_handler/rtl/alert_handler_reg_wrap.sv.tpl index c5de9bb34c041..43fbc4b78ca37 100644 --- a/hw/ip_templates/alert_handler/rtl/alert_handler_reg_wrap.sv.tpl +++ b/hw/ip_templates/alert_handler/rtl/alert_handler_reg_wrap.sv.tpl @@ -4,7 +4,20 @@ // // Breakout / remapping wrapper for register file. +% if racl_support: +module ${module_instance_name}_reg_wrap + import ${module_instance_name}_pkg::*; +#( + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 'b1, +<% +num_regs = 6 + 4 * n_alerts + 4 * 7 + n_classes * 14 +%>\ + parameter int unsigned RaclPolicySelVec[${num_regs}] = '{${num_regs}{0}} +) ( +% else: module ${module_instance_name}_reg_wrap import ${module_instance_name}_pkg::*; ( +% endif input clk_i, input rst_ni, input rst_shadowed_ni, @@ -20,6 +33,12 @@ module ${module_instance_name}_reg_wrap import ${module_instance_name}_pkg::*; ( input hw2reg_wrap_t hw2reg_wrap, // reg2hw output reg2hw_wrap_t reg2hw_wrap, +% if racl_support: + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, +% endif // bus integrity alert output logic fatal_integ_alert_o ); @@ -33,7 +52,15 @@ module ${module_instance_name}_reg_wrap import ${module_instance_name}_pkg::*; ( ${module_instance_name}_reg_pkg::${module_instance_name}_reg2hw_t reg2hw; ${module_instance_name}_reg_pkg::${module_instance_name}_hw2reg_t hw2reg; +% if racl_support: + ${module_instance_name}_reg_top #( + .EnableRacl(EnableRacl), + .RaclErrorRsp(RaclErrorRsp), + .RaclPolicySelVec(RaclPolicySelVec) + ) u_reg ( +% else: ${module_instance_name}_reg_top u_reg ( +% endif .clk_i, .rst_ni, .rst_shadowed_ni, @@ -41,6 +68,11 @@ module ${module_instance_name}_reg_wrap import ${module_instance_name}_pkg::*; ( .tl_o, .reg2hw, .hw2reg, + % if racl_support: + .racl_policies_i, + .racl_error_o, + .racl_error_log_o, + % endif .shadowed_storage_err_o(reg2hw_wrap.shadowed_err_storage), .shadowed_update_err_o(reg2hw_wrap.shadowed_err_update), .intg_err_o(fatal_integ_alert_o) diff --git a/hw/ip_templates/racl_ctrl/data/racl_ctrl.hjson.tpl b/hw/ip_templates/racl_ctrl/data/racl_ctrl.hjson.tpl index f05cb244f3c0e..e5d753592038f 100644 --- a/hw/ip_templates/racl_ctrl/data/racl_ctrl.hjson.tpl +++ b/hw/ip_templates/racl_ctrl/data/racl_ctrl.hjson.tpl @@ -30,7 +30,7 @@ {clock: "clk_i", reset: "rst_ni"}, ] bus_interfaces: [ - { protocol: "tlul", direction: "device" } + { protocol: "tlul", direction: "device", static_racl_support: true } ], alert_list: [ % if enable_shadow_reg: @@ -69,15 +69,36 @@ }, ], inter_signal_list: [ - { struct: "policies", + { struct: "racl_policy_vec", type: "uni", - name: "policies", + name: "racl_policies", act: "req", - package: "racl_pkg", + package: "top_racl_pkg", desc: ''' Policy vector distributed to the subscribing RACL IPs. ''' - }, + } + { struct: "logic", + type: "uni", + name: "racl_error", + act: "rcv", + width : "NumSubscribingIps", + desc: ''' + Error notification vector collecting errors from all subscribing IPs. + A 1 indicates the corresponding IP raised a RACL error and the error log needs to be collected. + Only one IP can raise an error at a time. + ''' + } + { struct: "racl_error_log", + type: "uni", + name: "racl_error_log", + act: "rcv", + width: "NumSubscribingIps" + package: "top_racl_pkg", + desc: ''' + Error log information from all IPs. + ''' + } ], registers: [ @@ -105,7 +126,7 @@ ''' } { bits: "2" - name: "write_read" + name: "read_access" resval: 0x0 desc: ''' 0: Write transfer was denied. diff --git a/hw/ip_templates/racl_ctrl/rtl/racl_ctrl.sv.tpl b/hw/ip_templates/racl_ctrl/rtl/racl_ctrl.sv.tpl index 48473b4a37cb8..5bcd280debe84 100644 --- a/hw/ip_templates/racl_ctrl/rtl/racl_ctrl.sv.tpl +++ b/hw/ip_templates/racl_ctrl/rtl/racl_ctrl.sv.tpl @@ -4,7 +4,8 @@ module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, - parameter int unsigned NumSubscribingIps = 1 + parameter int unsigned NumSubscribingIps = 1, + parameter bit RaclErrorRsp = 1'b1 ) ( input logic clk_i, input logic rst_ni, @@ -18,7 +19,7 @@ module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, // Output policy vector for distribution - output top_racl_pkg::racl_policy_vec_t policies_o, + output top_racl_pkg::racl_policy_vec_t racl_policies_o, // RACL violation information. input logic [NumSubscribingIps-1:0] racl_error_i, input top_racl_pkg::racl_error_log_t [NumSubscribingIps-1:0] racl_error_log_i @@ -38,21 +39,26 @@ module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( % if enable_shadow_reg: // SEC_CM: RACL_POLICY.CONFIG.SHADOW % endif - ${module_instance_name}_reg_top u_racl_ctrl_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), + ${module_instance_name}_reg_top u_racl_ctrl_reg #( + .EnableRacl ( 1'b1 ), + .RaclErrorRsp ( RaclErrorRsp ) + u_racl_ctrl_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), % if enable_shadow_reg: - .rst_shadowed_ni ( rst_shadowed_ni ), + .rst_shadowed_ni ( rst_shadowed_ni ), % endif - .tl_i ( tl_i ), - .tl_o ( tl_o ), - .reg2hw ( reg2hw ), - .hw2reg ( hw2reg ), + .tl_i ( tl_i ), + .tl_o ( tl_o ), + .reg2hw ( reg2hw ), + .hw2reg ( hw2reg ), % if enable_shadow_reg: - .shadowed_storage_err_o ( shadowed_storage_err ), - .shadowed_update_err_o ( shadowed_update_err ), + .shadowed_storage_err_o ( shadowed_storage_err ), + .shadowed_update_err_o ( shadowed_update_err ), % endif - .intg_err_o ( reg_intg_error ) + .racl_error_o ( racl_ctrl_racl_error ), + .racl_error_log_o ( racl_ctrl_racl_error_log ), + .intg_err_o ( reg_intg_error ) ); ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -114,7 +120,7 @@ module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( % endfor // Broadcast all policies via policy vector - assign policies_o = { + assign racl_policies_o = { % for policy in policies: policy_${policy['name'].lower()}${',' if not loop.last else ''} % endfor @@ -123,28 +129,32 @@ module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( ////////////////////////////////////////////////////////////////////////////////////////////////// // Error handling ////////////////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// // A RACL error can only happen for one IP at a time in one RACL domain. Therefore, it is // safe to OR all RACL error bits together and no arbitration is needed. This is true also // for the corresponding RACL role or Write/Read information. + `ASSERT(OneHotRaclError_A, $onehot0(racl_error_i)) + logic racl_error; - assign racl_error = |racl_error_i; + // A combined RACL error from external subscribing IPs in the racl_ctrl internal reg_top + assign racl_error = |racl_error_i | racl_ctrl_racl_error; top_racl_pkg::racl_role_t racl_error_role; top_racl_pkg::ctn_uid_t racl_error_ctn_uid; - logic racl_error_write_read; + logic racl_error_read_access; // Reduce all incoming error vectors to a single role and write/read bit. // Only a single IP can have a RACL error at one time. always_comb begin - racl_error_role = '0; - racl_error_ctn_uid = '0; - racl_error_write_read = 1'b0; + // Default to the racl_ctrl reg_top error information. Possible since only + // one error allowed at a time. + racl_error_role = racl_ctrl_racl_error_log.racl_role; + racl_error_ctn_uid = racl_ctrl_racl_error_log.ctn_uid; + racl_error_read_access = racl_ctrl_racl_error_log.read_access; for (int i = 0; i < NumSubscribingIps; i++) begin - racl_error_role |= racl_error_log_i[i].racl_role; - racl_error_ctn_uid |= racl_error_log_i[i].ctn_uid; - racl_error_write_read |= racl_error_log_i[i].write_read; + racl_error_role |= racl_error_log_i[i].racl_role; + racl_error_ctn_uid |= racl_error_log_i[i].ctn_uid; + racl_error_read_access |= racl_error_log_i[i].read_access; end end @@ -162,8 +172,8 @@ module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( assign hw2reg.error_log.overflow.d = ~clear_log; assign hw2reg.error_log.overflow.de = (reg2hw.error_log.valid.q & racl_error) | clear_log; - assign hw2reg.error_log.write_read.d = clear_log ? '0 : racl_error_write_read; - assign hw2reg.error_log.write_read.de = first_error | clear_log; + assign hw2reg.error_log.read_access.d = clear_log ? '0 : racl_error_read_access; + assign hw2reg.error_log.read_access.de = first_error | clear_log; assign hw2reg.error_log.role.d = clear_log ? '0 : racl_error_role; assign hw2reg.error_log.role.de = first_error | clear_log; diff --git a/hw/ip_templates/rv_plic/data/rv_plic.hjson.tpl b/hw/ip_templates/rv_plic/data/rv_plic.hjson.tpl index b926fea2fa39a..6f9e309edc1e8 100644 --- a/hw/ip_templates/rv_plic/data/rv_plic.hjson.tpl +++ b/hw/ip_templates/rv_plic/data/rv_plic.hjson.tpl @@ -38,7 +38,11 @@ ], clocking: [{clock: "clk_i", reset: "rst_ni"}], bus_interfaces: [ + % if racl_support: + { protocol: "tlul", direction: "device", racl_support: true } + % else: { protocol: "tlul", direction: "device" } + % endif ], param_list: [ @@ -96,6 +100,38 @@ package: "", width: "${target}" }, + % if racl_support: + { struct: "racl_policy_vec", + type: "uni", + name: "racl_policies", + act: "rcv", + package: "top_racl_pkg", + desc: ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + } + { struct: "logic", + type: "uni", + name: "racl_error", + act: "req", + width : "1", + desc: ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + } + { struct: "racl_error_log", + type: "uni", + name: "racl_error_log", + act: "req", + width: "1" + package: "top_racl_pkg", + desc: ''' + RACL error log information of this module. + ''' + } + % endif ] countermeasures: [ diff --git a/hw/ip_templates/rv_plic/data/rv_plic.tpldesc.hjson b/hw/ip_templates/rv_plic/data/rv_plic.tpldesc.hjson index 0e1cbeaec2545..f68b89c0bcd4b 100644 --- a/hw/ip_templates/rv_plic/data/rv_plic.tpldesc.hjson +++ b/hw/ip_templates/rv_plic/data/rv_plic.tpldesc.hjson @@ -33,5 +33,11 @@ type: "string" default: "rv_plic" } + { + name: "racl_support" + desc: "Enable RACL support" + type: "bool" + default: false + } ] } diff --git a/hw/ip_templates/rv_plic/rtl/rv_plic.sv.tpl b/hw/ip_templates/rv_plic/rtl/rv_plic.sv.tpl index 8a562a6778570..c3eb2795940a6 100644 --- a/hw/ip_templates/rv_plic/rtl/rv_plic.sv.tpl +++ b/hw/ip_templates/rv_plic/rtl/rv_plic.sv.tpl @@ -24,6 +24,15 @@ module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( // fully implemented yet (this would require instantiating pulse syncs // and routing the source clocks / resets to the PLIC). parameter logic [NumSrc-1:0] LevelEdgeTrig = '0, // 0: level, 1: edge +% if racl_support: + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 1'b1, +<% +from math import ceil +num_regs = src + ceil(src / 32) + target * ceil(src / 32) + 3 * target + 1 +%>\ + parameter int unsigned RaclPolicySelVec[${num_regs}] = '{${num_regs}{0}}, +% endif // derived parameter localparam int SRCW = $clog2(NumSrc) ) ( @@ -33,6 +42,13 @@ module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( // Bus Interface (device) input tlul_pkg::tl_h2d_t tl_i, output tlul_pkg::tl_d2h_t tl_o, +% if racl_support: + + // RACL interface + input top_racl_pkg::racl_policy_vec_t racl_policies_i, + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, +% endif // Interrupt Sources input [NumSrc-1:0] intr_src_i, @@ -229,7 +245,15 @@ module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( //////////////////////// // Limitation of register tool prevents the module from having flexibility to parameters // So, signals are manually tied at the top. +% if racl_support: + ${module_instance_name}_reg_top #( + .EnableRacl(EnableRacl), + .RaclErrorRsp(RaclErrorRsp), + .RaclPolicySelVec(RaclPolicySelVec) + ) u_reg ( +% else: ${module_instance_name}_reg_top u_reg ( +% endif .clk_i, .rst_ni, @@ -238,6 +262,13 @@ module ${module_instance_name} import ${module_instance_name}_reg_pkg::*; #( .reg2hw, .hw2reg, + % if racl_support: + + // RACL interface + .racl_policies_i, + .racl_error_o, + .racl_error_log_o, + % endif // SEC_CM: BUS.INTEGRITY .intg_err_o(alerts[0]) diff --git a/hw/ip_templates/rv_plic/rv_plic.core.tpl b/hw/ip_templates/rv_plic/rv_plic.core.tpl index 2e869b4dcaf4d..4b518fd1129f8 100644 --- a/hw/ip_templates/rv_plic/rv_plic.core.tpl +++ b/hw/ip_templates/rv_plic/rv_plic.core.tpl @@ -11,6 +11,9 @@ filesets: - ${instance_vlnv(f"lowrisc:ip:{module_instance_name}_component:0.1")} - lowrisc:ip:tlul - lowrisc:prim:subreg + % if racl_support: + - lowrisc:systems:top_racl_pkg + % endif files: - rtl/${module_instance_name}_reg_pkg.sv - rtl/${module_instance_name}_reg_top.sv diff --git a/hw/top_darjeeling/data/autogen/top_darjeeling.gen.hjson b/hw/top_darjeeling/data/autogen/top_darjeeling.gen.hjson index e913d7bcb5792..acfa66c0e13d5 100644 --- a/hw/top_darjeeling/data/autogen/top_darjeeling.gen.hjson +++ b/hw/top_darjeeling/data/autogen/top_darjeeling.gen.hjson @@ -568,6 +568,46 @@ top_signame: uart0_lsio_trigger index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } { name: tl struct: tl @@ -943,6 +983,46 @@ top_signame: i2c0_lsio_trigger index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: i2c0 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: i2c0 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: i2c0 + index: -1 + } { name: tl struct: tl @@ -18327,6 +18407,46 @@ top_signame: uart0_lsio_trigger index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } { name: tl struct: tl @@ -18530,6 +18650,46 @@ top_signame: i2c0_lsio_trigger index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: i2c0 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: i2c0 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: i2c0 + index: -1 + } { name: tl struct: tl diff --git a/hw/top_darjeeling/rtl/autogen/top_darjeeling.sv b/hw/top_darjeeling/rtl/autogen/top_darjeeling.sv index 111b4164111a9..337ed7f6780f9 100644 --- a/hw/top_darjeeling/rtl/autogen/top_darjeeling.sv +++ b/hw/top_darjeeling/rtl/autogen/top_darjeeling.sv @@ -987,6 +987,9 @@ module top_darjeeling #( // Inter-module signals .lsio_trigger_o(uart0_lsio_trigger), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(uart0_tl_req), .tl_o(uart0_tl_rsp), @@ -1109,6 +1112,9 @@ module top_darjeeling #( .ram_cfg_i(i2c_ram_1p_cfg_i), .ram_cfg_rsp_o(i2c_ram_1p_cfg_rsp_o), .lsio_trigger_o(i2c0_lsio_trigger), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(i2c0_tl_req), .tl_o(i2c0_tl_rsp), diff --git a/hw/top_darjeeling/rtl/autogen/top_racl_pkg.sv b/hw/top_darjeeling/rtl/autogen/top_racl_pkg.sv index 5f1bbf2ad59f8..1cbd2b0062c56 100644 --- a/hw/top_darjeeling/rtl/autogen/top_racl_pkg.sv +++ b/hw/top_darjeeling/rtl/autogen/top_racl_pkg.sv @@ -16,10 +16,10 @@ package top_racl_pkg; parameter int unsigned NrRaclPolicies = 1; // Number of RACL bits transferred - parameter int unsigned NrRaclBits = 4; + parameter int unsigned NrRaclBits = 1; // Number of CTN UID bits transferred - parameter int unsigned NrCtnUidBits = 8; + parameter int unsigned NrCtnUidBits = 1; // RACL role type binary encoded typedef logic [NrRaclBits-1:0] racl_role_t; @@ -39,12 +39,21 @@ package top_racl_pkg; // RACL policy vector for distributing RACL policies from the RACL widget to the subscribing IP typedef racl_policy_t [NrRaclPolicies-1:0] racl_policy_vec_t; + // Default policy vector for unconnected RACL IPs + parameter racl_policy_vec_t RACL_POLICY_VEC_DEFAULT = '0; + + // Default ROT Private read policy value + parameter racl_role_vec_t RACL_POLICY_ROT_PRIVATE_RD = 2'h0; + + // Default ROT Private write policy value + parameter racl_role_vec_t RACL_POLICY_ROT_PRIVATE_WR = 2'h0; + // RACL information logged in case of a denial typedef struct packed { racl_role_t racl_role; ctn_uid_t ctn_uid; // 0: Write access, 1: Read access - logic read_not_write; + logic read_access; } racl_error_log_t; // Extract RACL role bits from the TLUL reserved user bits @@ -53,7 +62,7 @@ package top_racl_pkg; logic unused_rsvd_bits; unused_rsvd_bits = ^{rsvd}; - return racl_role_t'(rsvd[11:8]); + return racl_role_t'(rsvd[0:0]); endfunction // Extract CTN UID bits from the TLUL reserved user bits @@ -62,7 +71,7 @@ package top_racl_pkg; logic unused_rsvd_bits; unused_rsvd_bits = ^{rsvd}; - return ctn_uid_t'(rsvd[7:0]); + return ctn_uid_t'(rsvd[0:0]); endfunction diff --git a/hw/top_darjeeling/templates/toplevel.sv.tpl b/hw/top_darjeeling/templates/toplevel.sv.tpl index 4d95b582c6290..7c28815da2a75 100644 --- a/hw/top_darjeeling/templates/toplevel.sv.tpl +++ b/hw/top_darjeeling/templates/toplevel.sv.tpl @@ -483,6 +483,17 @@ max_intrwidth = (max(len(x.name) for x in block.interrupts) %>\ % if m["param_list"] or block.alerts: ${m["type"]} #( + % if 'racl_mappings' in m: + .EnableRacl(1'b1), + .RaclErrorRsp(${"1'b1" if top['racl']['error_response'] else "1'b0"}), + % for if_name in m['racl_mappings'].keys(): +<% if_suffix = f"_{if_name.upper()}" if if_name else "" %>\ + .RaclPolicySelVec(top_racl_pkg::RACL_POLICY_SEL_${m["name"].upper()}${if_suffix}), + % endfor + % endif + % if m['type'].startswith('racl_ctrl'): + .RaclErrorRsp(${"1'b1" if top['racl']['error_response'] else "1'b0"}), + % endif % if block.alerts: <% w = len(block.alerts) diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson index 764decea9a211..404de702237f0 100644 --- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson +++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson @@ -629,6 +629,46 @@ inst_name: uart0 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } { name: tl struct: tl @@ -694,6 +734,46 @@ inst_name: uart1 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart1 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart1 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart1 + index: -1 + } { name: tl struct: tl @@ -759,6 +839,46 @@ inst_name: uart2 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart2 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart2 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart2 + index: -1 + } { name: tl struct: tl @@ -824,6 +944,46 @@ inst_name: uart3 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart3 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart3 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart3 + index: -1 + } { name: tl struct: tl @@ -1171,6 +1331,46 @@ inst_name: i2c0 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: i2c0 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: i2c0 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: i2c0 + index: -1 + } { name: tl struct: tl @@ -1274,6 +1474,46 @@ inst_name: i2c1 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: i2c1 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: i2c1 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: i2c1 + index: -1 + } { name: tl struct: tl @@ -1377,6 +1617,46 @@ inst_name: i2c2 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: i2c2 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: i2c2 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: i2c2 + index: -1 + } { name: tl struct: tl @@ -4344,6 +4624,46 @@ param_list: [] inter_signal_list: [ + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: pwm_aon + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: pwm_aon + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: pwm_aon + index: -1 + } { name: tl struct: tl @@ -17453,6 +17773,46 @@ inst_name: uart0 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } { name: tl struct: tl @@ -17480,6 +17840,46 @@ inst_name: uart1 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart1 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart1 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart1 + index: -1 + } { name: tl struct: tl @@ -17507,6 +17907,46 @@ inst_name: uart2 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart2 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart2 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart2 + index: -1 + } { name: tl struct: tl @@ -17534,6 +17974,46 @@ inst_name: uart3 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart3 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart3 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart3 + index: -1 + } { name: tl struct: tl @@ -17712,6 +18192,46 @@ inst_name: i2c0 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: i2c0 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: i2c0 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: i2c0 + index: -1 + } { name: tl struct: tl @@ -17761,6 +18281,46 @@ inst_name: i2c1 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: i2c1 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: i2c1 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: i2c1 + index: -1 + } { name: tl struct: tl @@ -17810,6 +18370,46 @@ inst_name: i2c2 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: i2c2 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: i2c2 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: i2c2 + index: -1 + } { name: tl struct: tl @@ -19737,6 +20337,46 @@ top_signame: adc_ctrl_aon_tl index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: pwm_aon + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: pwm_aon + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: pwm_aon + index: -1 + } { name: tl struct: tl diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv index 7df4840f98e34..198c1fde25a17 100644 --- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv +++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv @@ -1085,6 +1085,9 @@ module top_earlgrey #( // Inter-module signals .lsio_trigger_o(), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(uart0_tl_req), .tl_o(uart0_tl_rsp), @@ -1119,6 +1122,9 @@ module top_earlgrey #( // Inter-module signals .lsio_trigger_o(), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(uart1_tl_req), .tl_o(uart1_tl_rsp), @@ -1153,6 +1159,9 @@ module top_earlgrey #( // Inter-module signals .lsio_trigger_o(), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(uart2_tl_req), .tl_o(uart2_tl_rsp), @@ -1187,6 +1196,9 @@ module top_earlgrey #( // Inter-module signals .lsio_trigger_o(), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(uart3_tl_req), .tl_o(uart3_tl_rsp), @@ -1309,6 +1321,9 @@ module top_earlgrey #( .ram_cfg_i(ast_ram_1p_cfg), .ram_cfg_rsp_o(), .lsio_trigger_o(), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(i2c0_tl_req), .tl_o(i2c0_tl_rsp), @@ -1355,6 +1370,9 @@ module top_earlgrey #( .ram_cfg_i(ast_ram_1p_cfg), .ram_cfg_rsp_o(), .lsio_trigger_o(), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(i2c1_tl_req), .tl_o(i2c1_tl_rsp), @@ -1401,6 +1419,9 @@ module top_earlgrey #( .ram_cfg_i(ast_ram_1p_cfg), .ram_cfg_rsp_o(), .lsio_trigger_o(), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(i2c2_tl_req), .tl_o(i2c2_tl_rsp), @@ -1988,6 +2009,9 @@ module top_earlgrey #( .alert_rx_i ( alert_rx[29:29] ), // Inter-module signals + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(pwm_aon_tl_req), .tl_o(pwm_aon_tl_rsp), diff --git a/hw/top_earlgrey/rtl/autogen/top_racl_pkg.sv b/hw/top_earlgrey/rtl/autogen/top_racl_pkg.sv index da876a03816ac..141fbfd5b8749 100644 --- a/hw/top_earlgrey/rtl/autogen/top_racl_pkg.sv +++ b/hw/top_earlgrey/rtl/autogen/top_racl_pkg.sv @@ -16,10 +16,10 @@ package top_racl_pkg; parameter int unsigned NrRaclPolicies = 1; // Number of RACL bits transferred - parameter int unsigned NrRaclBits = 4; + parameter int unsigned NrRaclBits = 1; // Number of CTN UID bits transferred - parameter int unsigned NrCtnUidBits = 8; + parameter int unsigned NrCtnUidBits = 1; // RACL role type binary encoded typedef logic [NrRaclBits-1:0] racl_role_t; @@ -39,12 +39,21 @@ package top_racl_pkg; // RACL policy vector for distributing RACL policies from the RACL widget to the subscribing IP typedef racl_policy_t [NrRaclPolicies-1:0] racl_policy_vec_t; + // Default policy vector for unconnected RACL IPs + parameter racl_policy_vec_t RACL_POLICY_VEC_DEFAULT = '0; + + // Default ROT Private read policy value + parameter racl_role_vec_t RACL_POLICY_ROT_PRIVATE_RD = 2'h0; + + // Default ROT Private write policy value + parameter racl_role_vec_t RACL_POLICY_ROT_PRIVATE_WR = 2'h0; + // RACL information logged in case of a denial typedef struct packed { racl_role_t racl_role; ctn_uid_t ctn_uid; // 0: Write access, 1: Read access - logic read_not_write; + logic read_access; } racl_error_log_t; // Extract RACL role bits from the TLUL reserved user bits @@ -53,7 +62,7 @@ package top_racl_pkg; logic unused_rsvd_bits; unused_rsvd_bits = ^{rsvd}; - return racl_role_t'(rsvd[11:8]); + return racl_role_t'(rsvd[0:0]); endfunction // Extract CTN UID bits from the TLUL reserved user bits @@ -62,7 +71,7 @@ package top_racl_pkg; logic unused_rsvd_bits; unused_rsvd_bits = ^{rsvd}; - return ctn_uid_t'(rsvd[7:0]); + return ctn_uid_t'(rsvd[0:0]); endfunction diff --git a/hw/top_earlgrey/templates/toplevel.sv.tpl b/hw/top_earlgrey/templates/toplevel.sv.tpl index 1dd9ed3e854c8..fc7498ad6acbb 100644 --- a/hw/top_earlgrey/templates/toplevel.sv.tpl +++ b/hw/top_earlgrey/templates/toplevel.sv.tpl @@ -502,6 +502,17 @@ max_intrwidth = (max(len(x.name) for x in block.interrupts) %>\ % if m["param_list"] or block.alerts: ${m["type"]} #( + % if m.get('racl_mappings'): + .EnableRacl(1'b1), + .RaclErrorRsp(${"1'b1" if top['racl']['error_response'] else "1'b0"}), + % for if_name in m['racl_mappings'].keys(): +<% if_suffix = f"_{if_name.upper()}" if if_name else "" %>\ + .RaclPolicySelVec(top_racl_pkg::RACL_POLICY_SEL_${m["name"].upper()}${if_suffix}), + % endfor + % endif + % if m['type'].startswith('racl_ctrl'): + .RaclErrorRsp(${"1'b1" if top['racl']['error_response'] else "1'b0"}), + % endif % if block.alerts: <% w = len(block.alerts) diff --git a/hw/top_englishbreakfast/data/autogen/top_englishbreakfast.gen.hjson b/hw/top_englishbreakfast/data/autogen/top_englishbreakfast.gen.hjson index 20bbed911ad86..6bcc16804290c 100644 --- a/hw/top_englishbreakfast/data/autogen/top_englishbreakfast.gen.hjson +++ b/hw/top_englishbreakfast/data/autogen/top_englishbreakfast.gen.hjson @@ -495,6 +495,46 @@ inst_name: uart0 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } { name: tl struct: tl @@ -560,6 +600,46 @@ inst_name: uart1 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart1 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart1 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart1 + index: -1 + } { name: tl struct: tl @@ -8735,6 +8815,46 @@ inst_name: uart0 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart0 + index: -1 + } { name: tl struct: tl @@ -8762,6 +8882,46 @@ inst_name: uart1 index: -1 } + { + name: racl_policies + desc: + ''' + Incoming RACL policy vector from a racl_ctrl instance. + The policy selection vector (parameter) selects the policy for each register. + ''' + struct: racl_policy_vec + package: top_racl_pkg + type: uni + act: rcv + width: 1 + inst_name: uart1 + index: -1 + } + { + name: racl_error + desc: + ''' + RACL error indication signal. + If 1, the error log contains valid information. + ''' + struct: logic + type: uni + act: req + width: 1 + inst_name: uart1 + index: -1 + } + { + name: racl_error_log + desc: RACL error log information of this module. + struct: racl_error_log + package: top_racl_pkg + type: uni + act: req + width: 1 + inst_name: uart1 + index: -1 + } { name: tl struct: tl diff --git a/hw/top_englishbreakfast/rtl/autogen/top_englishbreakfast.sv b/hw/top_englishbreakfast/rtl/autogen/top_englishbreakfast.sv index 793fb800a1fbc..4e91225e4822f 100644 --- a/hw/top_englishbreakfast/rtl/autogen/top_englishbreakfast.sv +++ b/hw/top_englishbreakfast/rtl/autogen/top_englishbreakfast.sv @@ -571,6 +571,9 @@ module top_englishbreakfast #( // Inter-module signals .lsio_trigger_o(), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(uart0_tl_req), .tl_o(uart0_tl_rsp), @@ -605,6 +608,9 @@ module top_englishbreakfast #( // Inter-module signals .lsio_trigger_o(), + .racl_policies_i(top_racl_pkg::RACL_POLICY_VEC_DEFAULT), + .racl_error_o(), + .racl_error_log_o(), .tl_i(uart1_tl_req), .tl_o(uart1_tl_rsp), diff --git a/hw/top_englishbreakfast/rtl/autogen/top_racl_pkg.sv b/hw/top_englishbreakfast/rtl/autogen/top_racl_pkg.sv index f984ae1801590..c0ce43815e217 100644 --- a/hw/top_englishbreakfast/rtl/autogen/top_racl_pkg.sv +++ b/hw/top_englishbreakfast/rtl/autogen/top_racl_pkg.sv @@ -16,10 +16,10 @@ package top_racl_pkg; parameter int unsigned NrRaclPolicies = 1; // Number of RACL bits transferred - parameter int unsigned NrRaclBits = 4; + parameter int unsigned NrRaclBits = 1; // Number of CTN UID bits transferred - parameter int unsigned NrCtnUidBits = 8; + parameter int unsigned NrCtnUidBits = 1; // RACL role type binary encoded typedef logic [NrRaclBits-1:0] racl_role_t; @@ -39,12 +39,21 @@ package top_racl_pkg; // RACL policy vector for distributing RACL policies from the RACL widget to the subscribing IP typedef racl_policy_t [NrRaclPolicies-1:0] racl_policy_vec_t; + // Default policy vector for unconnected RACL IPs + parameter racl_policy_vec_t RACL_POLICY_VEC_DEFAULT = '0; + + // Default ROT Private read policy value + parameter racl_role_vec_t RACL_POLICY_ROT_PRIVATE_RD = 2'h0; + + // Default ROT Private write policy value + parameter racl_role_vec_t RACL_POLICY_ROT_PRIVATE_WR = 2'h0; + // RACL information logged in case of a denial typedef struct packed { racl_role_t racl_role; ctn_uid_t ctn_uid; // 0: Write access, 1: Read access - logic read_not_write; + logic read_access; } racl_error_log_t; // Extract RACL role bits from the TLUL reserved user bits @@ -53,7 +62,7 @@ package top_racl_pkg; logic unused_rsvd_bits; unused_rsvd_bits = ^{rsvd}; - return racl_role_t'(rsvd[11:8]); + return racl_role_t'(rsvd[0:0]); endfunction // Extract CTN UID bits from the TLUL reserved user bits @@ -62,7 +71,7 @@ package top_racl_pkg; logic unused_rsvd_bits; unused_rsvd_bits = ^{rsvd}; - return ctn_uid_t'(rsvd[7:0]); + return ctn_uid_t'(rsvd[0:0]); endfunction diff --git a/util/raclgen/lib.py b/util/raclgen/lib.py index a42e6471a8f10..cac6cb452f0f3 100644 --- a/util/raclgen/lib.py +++ b/util/raclgen/lib.py @@ -3,43 +3,80 @@ # SPDX-License-Identifier: Apache-2.0 import hjson +import logging import sys -from reggen.validate import check_keys from typing import Dict +from typing import Optional +from reggen.ip_block import IpBlock +from reggen.validate import check_keys # Required fields for the RACL hjson racl_required = { + 'error_response': [ + 'pb', + 'When true, return TLUL error on denied RACL access, otherwise not' + ], + 'role_bit_lsb': ['d', 'RACL role bit LSB within the TLUL user bit vector'], + 'role_bit_msb': ['d', 'RACL role bit MSB within the TLUL user bit vector'], + 'ctn_uid_bit_lsb': ['d', 'CTN UID bit LSB within the TLUL user bit vector'], + 'ctn_uid_bit_msb': ['d', 'CTN UID bit MSB within the TLUL user bit vector'], 'roles': ['l', 'List, specifying all RACL roles'], 'policies': ['g', 'Dict, specifying the policies of all RACL groups'] } - # Default configuration to render the RACL package for systems that don't use RACL but need the # type definitions DEFAULT_RACL_CONFIG = { + 'role_bit_lsb': 0, + 'role_bit_msb': 0, + 'ctn_uid_bit_lsb': 0, + 'ctn_uid_bit_msb': 0, + 'nr_role_bits': 1, + 'nr_ctn_uid_bits': 1, 'nr_policies': 1, 'policies': {}, + 'rot_private_policy_rd': 0, + 'rot_private_policy_wr': 0 } -def parse_racl_config(config_path: str) -> Dict[str, object]: +def _read_hjson(filename: str) -> Dict[str, object]: try: - with open(config_path, 'r') as f_racl_config: - racl_config = hjson.load(f_racl_config) + with open(filename, 'r') as f_racl_config: + return hjson.load(f_racl_config) + except ValueError: + logging.error(f'Error parsing HJSON config file {filename}') + raise SystemExit(sys.exc_info()[1]) except OSError: raise SystemExit(sys.exc_info()[1]) + +def parse_racl_config(config_path: str) -> Dict[str, object]: + racl_config = _read_hjson(config_path) + # TODO(#25690) Further sanity checks on the parsed RACL config error = check_keys(racl_config, racl_required, [], [], 'RACL Config') if error: raise SystemExit(f"Error occurred while validating {config_path}") + if racl_config['role_bit_lsb'] > racl_config['role_bit_msb']: + raise ValueError('Invalid RACL role bit configuration LSB > MSB') + if racl_config['ctn_uid_bit_lsb'] > racl_config['ctn_uid_bit_msb']: + raise ValueError('Invalid RACL CTN UID bit configuration LSB > MSB') + + racl_config['nr_role_bits'] = racl_config['role_bit_msb'] - racl_config['role_bit_lsb'] + 1 + racl_config['nr_ctn_uid_bits'] = racl_config['ctn_uid_bit_msb'] - \ + racl_config['ctn_uid_bit_lsb'] + 1 + # Determine the maximum number of policies over all RACL groups for RTL # RTL needs to create the vectors based on the largest group - racl_config['nr_policies'] = max(len(policies) for policies in racl_config['policies'].values()) + racl_config['nr_policies'] = max( + len(policies) for policies in racl_config['policies'].values()) + rot_private_policy = None for racl_group, policies in racl_config['policies'].items(): for policy in policies: + def compute_policy_value(permission: str) -> int: permission_value = 0 for role in policy[permission]: @@ -49,4 +86,70 @@ def compute_policy_value(permission: str) -> int: policy['rd_default'] = compute_policy_value('allowed_rd') policy['wr_default'] = compute_policy_value('allowed_wr') + if policy.get('rot_private'): + if rot_private_policy: + raise ValueError('Only one policy can be the ROT_PRIVATE policy') + rot_private_policy = policy + + if not rot_private_policy: + raise ValueError('No ROT_PRIVATE policy defined') + + # Get the default ROT private policy for static RACL protection of the racl_ctrl IP(s) + racl_config['rot_private_policy_rd'] = rot_private_policy['rd_default'] + racl_config['rot_private_policy_wr'] = rot_private_policy['wr_default'] + return racl_config + + +def parse_racl_mapping(racl_config: Dict[str, object], mapping_path: str, + if_name: Optional[str], + ip_block: IpBlock) -> Dict[str, int]: + mapping = _read_hjson(mapping_path) + parsed_register_mapping = {} + + # Mapping must be a dict with a single entry: + # RACL_GROUP => register mapping + if len(mapping) != 1: + raise SystemExit('Mapping file must be a single-element dict mapping ' + 'the RACL group to the register mapping') + racl_group, register_mapping = list(mapping.items())[0] + + if not isinstance(racl_group, str): + raise SystemExit('RACL group must be a string') + if not isinstance(register_mapping, dict): + raise SystemExit('Register mapping must be a a dict') + + # Special handling of the all star assignment: + # "*": POLICY + # Assigns all registers to a given policy + if list(register_mapping.keys()) == ["*"]: + policy_name = register_mapping["*"] + + if racl_group not in racl_config['policies']: + raise SystemExit( + f'RACL group {racl_group} not defined in RACL config') + + policy_idx = -1 + for idx, policy in enumerate(racl_config['policies'][racl_group]): + if policy['name'] == policy_name: + policy_idx = idx + break + + if policy_idx == -1: + raise SystemExit( + f'RACL policy {policy_name} not defined in RACL config ' + f'for group {racl_group}') + + reg_block = ip_block.reg_blocks.get(if_name) + if not reg_block: + raise SystemExit( + f"Register interface {if_name} not defined in in {ip_block['name']}" + ) + + for reg in reg_block.flat_regs: + parsed_register_mapping[reg.name] = policy_idx + else: + # General case not yet implemented + assert False + + return parsed_register_mapping diff --git a/util/reggen/bus_interfaces.py b/util/reggen/bus_interfaces.py index b3dc380b61bd1..37ec56b5b52da 100644 --- a/util/reggen/bus_interfaces.py +++ b/util/reggen/bus_interfaces.py @@ -19,10 +19,16 @@ def __init__(self, named_devices: List[str], device_async: Dict[Optional[str], str], device_hier_paths: Dict[Optional[str], str], - racl_support: Dict[Optional[str], bool]): + racl_support: Dict[Optional[str], bool], + static_racl_support: Dict[Optional[str], bool]): assert has_unnamed_device or named_devices assert len(named_hosts) == len(set(named_hosts)) assert len(named_devices) == len(set(named_devices)) + # Ensure that for all bus interfaces static and dynamic RACL support are not set at the + # same time + assert racl_support.keys() == static_racl_support.keys() + for if_name, if_racl_support in racl_support.items(): + assert not (if_racl_support and static_racl_support[if_name]) self.has_unnamed_host = has_unnamed_host self.named_hosts = named_hosts @@ -32,6 +38,7 @@ def __init__(self, self.device_async = device_async self.device_hier_paths = device_hier_paths self.racl_support = racl_support + self.static_racl_support = static_racl_support @staticmethod def from_raw(raw: object, where: str) -> 'BusInterfaces': @@ -44,12 +51,14 @@ def from_raw(raw: object, where: str) -> 'BusInterfaces': device_async = {} device_hier_paths = {} racl_support_map = {} + static_racl_support_map = {} for idx, raw_entry in enumerate(check_list(raw, where)): entry_what = 'entry {} of {}'.format(idx + 1, where) ed = check_keys(raw_entry, entry_what, ['protocol', 'direction'], - ['name', 'async', 'hier_path', 'racl_support']) + ['name', 'async', 'hier_path', + 'racl_support', 'static_racl_support']) protocol = check_str(ed['protocol'], 'protocol field of ' + entry_what) @@ -74,6 +83,8 @@ def from_raw(raw: object, where: str) -> 'BusInterfaces': racl_support = check_optional_bool(ed.get('racl_support'), 'racl_support field of ' + entry_what) + static_racl_support = check_optional_bool(ed.get('static_racl_support'), + 'static_racl_support field of ' + entry_what) if direction == 'host': if name is None: @@ -117,14 +128,19 @@ def from_raw(raw: object, where: str) -> 'BusInterfaces': else: device_hier_paths[name] = 'u_reg' + if racl_support and static_racl_support: + raise ValueError("Device interface cannot support both static and dynamic RACL") + racl_support_map[name] = bool(racl_support) + static_racl_support_map[name] = bool(static_racl_support) if not (has_unnamed_device or named_devices): raise ValueError('No device interface at ' + where) return BusInterfaces(has_unnamed_host, named_hosts, host_async, has_unnamed_device, named_devices, - device_async, device_hier_paths, racl_support_map) + device_async, device_hier_paths, + racl_support_map, static_racl_support_map) def has_host(self) -> bool: return bool(self.has_unnamed_host or self.named_hosts) diff --git a/util/reggen/reg_top.sv.tpl b/util/reggen/reg_top.sv.tpl index 1e5ba756e7991..a1da4a3347ef3 100644 --- a/util/reggen/reg_top.sv.tpl +++ b/util/reggen/reg_top.sv.tpl @@ -33,7 +33,9 @@ reg2hw_t = gen_rtl.get_iface_tx_type(block, if_name, False) hw2reg_t = gen_rtl.get_iface_tx_type(block, if_name, True) - racl_support = block.bus_interfaces.racl_support[if_name] + dynamic_racl_support = block.bus_interfaces.racl_support[if_name] + static_racl_support = block.bus_interfaces.static_racl_support[if_name] + racl_support = dynamic_racl_support or static_racl_support win_array_decl = f' [{num_wins}]' if num_wins > 1 else '' @@ -120,8 +122,11 @@ module ${mod_name}${' (' if not racl_support else ''} % if racl_support: # ( - parameter bit EnableRacl = 1'b0, - parameter bit RaclErrorRsp = 1'b1 + parameter bit EnableRacl = 1'b0, + parameter bit RaclErrorRsp = 1'b1${"," if dynamic_racl_support else ""} + % if dynamic_racl_support: + parameter int unsigned RaclPolicySelVec[${len(rb.flat_regs)}] = '{${len(rb.flat_regs)}{0}} + % endif ) ( % endif input clk_i, @@ -157,10 +162,11 @@ module ${mod_name}${' (' if not racl_support else ''} %endif % if racl_support: // RACL interface +% if dynamic_racl_support: input top_racl_pkg::racl_policy_vec_t racl_policies_i, - input integer racl_policy_sel_vec_i[${len(rb.flat_regs)}], - output logic racl_error_o, - output top_racl_pkg::racl_error_log_t racl_error_log_o, +% endif + output logic racl_error_o, + output top_racl_pkg::racl_error_log_t racl_error_log_o, % endif // Integrity check errors @@ -674,7 +680,7 @@ ${finst_gen(sr, field, finst_name, fsig_name, fidx)} assign racl_role = top_racl_pkg::tlul_extract_racl_role_bits(tl_i.a_user.rsvd); prim_onehot_enc #( - .OneHotWidth( $bits(prim_onehot_enc) ) + .OneHotWidth( $bits(top_racl_pkg::racl_role_vec_t) ) ) u_racl_role_encode ( .in_i ( racl_role ), .en_i ( 1'b1 ), @@ -699,11 +705,22 @@ ${finst_gen(sr, field, finst_name, fsig_name, fidx)} % if racl_support: if (EnableRacl) begin : gen_racl_hit - % for i,r in enumerate(regs_flat): -<% slice = '{}'.format(i).rjust(max_regs_char) %>\ - racl_addr_hit_read [${slice}] = addr_hit[${slice}] & (|(racl_policies_i[racl_policy_sel_vec_i[${slice}]].read_perm & racl_role_vec)); - racl_addr_hit_write[${slice}] = addr_hit[${slice}] & (|(racl_policies_i[racl_policy_sel_vec_i[${slice}]].write_perm & racl_role_vec)); - % endfor + for (int unsigned slice_idx = 0; slice_idx < ${len(regs_flat)}; slice_idx++) begin + % if dynamic_racl_support: + racl_addr_hit_read[slice_idx] = + addr_hit[slice_idx] & (|(racl_policies_i[RaclPolicySelVec[slice_idx]].read_perm + & racl_role_vec)); + racl_addr_hit_write[slice_idx] = + addr_hit[slice_idx] & (|(racl_policies_i[RaclPolicySelVec[slice_idx]].write_perm + & racl_role_vec)); + % else: + // Static RACL protection with ROT_PRIVATE policy + racl_addr_hit_read[slice_idx] = + addr_hit[slice_idx] & (|(top_racl_pkg::RACL_POLICY_ROT_PRIVATE_RD & racl_role_vec)); + racl_addr_hit_write[slice_idx] = + addr_hit[slice_idx] & (|(top_racl_pkg::RACL_POLICY_ROT_PRIVATE_WR & racl_role_vec)); + % endif + end end else begin : gen_no_racl racl_addr_hit_read = addr_hit; racl_addr_hit_write = addr_hit; @@ -718,11 +735,11 @@ ${finst_gen(sr, field, finst_name, fsig_name, fidx)} assign racl_error_log_o.racl_role = racl_role; if (EnableRacl) begin : gen_racl_log - assign racl_error_log_o.ctn_uid = top_racl_pkg::tlul_extract_ctn_uid_bits(tl_i.a_user.rsvd); - assign racl_error_log_o.read_not_write = tl_i.a_opcode == tlul_pkg::Get; + assign racl_error_log_o.ctn_uid = top_racl_pkg::tlul_extract_ctn_uid_bits(tl_i.a_user.rsvd); + assign racl_error_log_o.read_access = tl_i.a_opcode == tlul_pkg::Get; end else begin : gen_no_racl_log - assign racl_error_log_o.ctn_uid = '0; - assign racl_error_log_o.read_not_write = 1'b0; + assign racl_error_log_o.ctn_uid = '0; + assign racl_error_log_o.read_access = 1'b0; end % endif @@ -912,6 +929,10 @@ ${rdata_gen(f, r.name.lower() + "_" + f.name.lower())}\ assign unused_wdata = ^reg_wdata; assign unused_be = ^reg_be; % endif +% if dynamic_racl_support: + logic unused_policy_sel; + assign unused_policy_sel = ^racl_policies_i; +% endif % if rb.all_regs: // Assertions for Register Interface diff --git a/util/topgen.py b/util/topgen.py index f2a8ec82d4d64..ee1ec663d876c 100755 --- a/util/topgen.py +++ b/util/topgen.py @@ -22,7 +22,7 @@ from design.lib.OtpMemMap import OtpMemMap from mako import exceptions from mako.template import Template -from raclgen.lib import DEFAULT_RACL_CONFIG, parse_racl_config +from raclgen.lib import DEFAULT_RACL_CONFIG, parse_racl_config, parse_racl_mapping from reggen import access, gen_rtl, gen_sec_cm_testplan, window from reggen.countermeasure import CounterMeasure from reggen.inter_signal import InterSignal @@ -582,13 +582,23 @@ def generate_ac_range_check(topcfg: Dict[str, object], out_path: Path) -> None: # Generate RACL collateral -def generate_racl(topcfg: Dict[str, object], out_path: Path) -> None: +def generate_racl(topcfg: Dict[str, object], name_to_block: Dict[str, IpBlock], + out_path: Path) -> None: # Not all tops use RACL if 'racl_config' not in topcfg: return + # Read the top-level RACL information topcfg['racl'] = parse_racl_config(topcfg['racl_config']) + # Generate the RACL mappings for all subscribing IPs + for m in topcfg['module']: + for if_name, mapping_path in m.get('racl_mappings', {}).items(): + m['racl_mappings'][if_name] = parse_racl_mapping(topcfg['racl'], + mapping_path, + if_name, + name_to_block[m['type']]) + log.info('Generating RACL Control IP with ipgen') topname = topcfg['name'] @@ -604,11 +614,11 @@ def generate_racl(topcfg: Dict[str, object], out_path: Path) -> None: policies = topcfg['racl']['policies'][racl_group] params = { - "module_instance_name": racl_ctrl["type"], - "nr_role_bits": 4, - "nr_ctn_uid_bits": 5, - "nr_policies": len(policies), - "policies": policies + 'module_instance_name': racl_ctrl['type'], + 'nr_role_bits': topcfg['racl']['nr_role_bits'], + 'nr_ctn_uid_bits': topcfg['racl']['nr_ctn_uid_bits'], + 'nr_policies': len(policies), + 'policies': policies } ipgen_render("racl_ctrl", topname, params, out_path) @@ -973,7 +983,7 @@ def _process_top( generate_ac_range_check(completecfg, out_path) # Generate RACL collateral - generate_racl(completecfg, out_path) + generate_racl(completecfg, name_to_block, out_path) # Generate top only modules # These modules are not ipgen, but are not in hw/ip @@ -1352,6 +1362,7 @@ def render_template(template_path: str, rendered_path: Path, render_template(TOPGEN_TEMPLATE_PATH / 'toplevel_racl_pkg.sv.tpl', out_path / 'rtl' / 'autogen' / 'top_racl_pkg.sv', gencmd=gencmd_sv, + topcfg=completecfg, racl_config=racl_config) # The C / SV file needs some complex information, so we initialize this diff --git a/util/topgen/intermodule.py b/util/topgen/intermodule.py index 6ea2211ad1602..47cbb77d00c77 100644 --- a/util/topgen/intermodule.py +++ b/util/topgen/intermodule.py @@ -248,6 +248,34 @@ def autoconnect(topcfg: OrderedDict, name_to_block: Dict[str, IpBlock]): for xbar in topcfg["xbar"]: autoconnect_xbar(topcfg, name_to_block, xbar) + # Auto connect RACL subscribing IPs to the associated racl_ctrl IP + if 'racl' in topcfg: + # Find the RACL control of the defined group. This currently only works for the default + # RACL ctrl module when there is a single instance. This limitation currently comes from + # ipgen and is tracked in #25673 + racl_ctrl = lib.find_module(topcfg['module'], 'racl_ctrl') + if not racl_ctrl: + raise ValueError('No RACL Control module found') + + # Determine all subscribing RACL modules + for m in topcfg['module']: + for racl_group in m.get('racl_mappings', {}).keys(): + add_intermodule_connection(obj=topcfg, + req_m=racl_ctrl['name'], + req_s="racl_policies", + rsp_m=m['name'], + rsp_s="racl_policies") + add_intermodule_connection(obj=topcfg, + req_m=racl_ctrl['name'], + req_s="racl_error", + rsp_m=m['name'], + rsp_s="racl_error") + add_intermodule_connection(obj=topcfg, + req_m=racl_ctrl['name'], + req_s="racl_error_log", + rsp_m=m['name'], + rsp_s="racl_error_log") + def _get_default_name(sig, suffix): """Generate default for a net if one does not already exist. diff --git a/util/topgen/merge.py b/util/topgen/merge.py index d9b0e53734230..15020d5669a3a 100644 --- a/util/topgen/merge.py +++ b/util/topgen/merge.py @@ -240,6 +240,16 @@ def elaborate_instance(instance, block: IpBlock): raise ValueError(f'generate_dif contains invalid value {instance["generate_dif"]}') instance['generate_dif'] = converted_value + # An instance can either have a 'racl_mapping' or 'racl_mappings' but can't have both. + # 'racl_mapping' is used when the device only has a single register interface and + # 'racl_mappings' when there are more. Translate to always use unified racl_mappings entry. + racl_mapping = instance.get('racl_mapping') + if racl_mapping is not None: + if instance.get('racl_mappings') is not None: + raise ValueError("Cannot specify both 'racl_mapping' and 'racl_mappings'") + del instance['racl_mapping'] + instance['racl_mappings'] = {None: racl_mapping} + # TODO: Replace this part to be configurable from Hjson or template predefined_modules = { diff --git a/util/topgen/templates/toplevel_racl_pkg.sv.tpl b/util/topgen/templates/toplevel_racl_pkg.sv.tpl index bc75b4ba6e2a0..3c0fa21458dc4 100644 --- a/util/topgen/templates/toplevel_racl_pkg.sv.tpl +++ b/util/topgen/templates/toplevel_racl_pkg.sv.tpl @@ -2,16 +2,17 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 ${gencmd} +<% racl_role_vec_len = 2 ** racl_config['nr_role_bits'] %>\ package top_racl_pkg; // Number of RACL policies used parameter int unsigned NrRaclPolicies = ${racl_config['nr_policies']}; // Number of RACL bits transferred - parameter int unsigned NrRaclBits = 4; + parameter int unsigned NrRaclBits = ${racl_config['nr_role_bits']}; // Number of CTN UID bits transferred - parameter int unsigned NrCtnUidBits = 8; + parameter int unsigned NrCtnUidBits = ${racl_config['nr_ctn_uid_bits']}; // RACL role type binary encoded typedef logic [NrRaclBits-1:0] racl_role_t; @@ -31,12 +32,21 @@ package top_racl_pkg; // RACL policy vector for distributing RACL policies from the RACL widget to the subscribing IP typedef racl_policy_t [NrRaclPolicies-1:0] racl_policy_vec_t; + // Default policy vector for unconnected RACL IPs + parameter racl_policy_vec_t RACL_POLICY_VEC_DEFAULT = '0; + + // Default ROT Private read policy value + parameter racl_role_vec_t RACL_POLICY_ROT_PRIVATE_RD = ${racl_role_vec_len}'h${f"{racl_config['rot_private_policy_rd']:x}"}; + + // Default ROT Private write policy value + parameter racl_role_vec_t RACL_POLICY_ROT_PRIVATE_WR = ${racl_role_vec_len}'h${f"{racl_config['rot_private_policy_wr']:x}"}; + // RACL information logged in case of a denial typedef struct packed { racl_role_t racl_role; ctn_uid_t ctn_uid; // 0: Write access, 1: Read access - logic read_not_write; + logic read_access; } racl_error_log_t; // Extract RACL role bits from the TLUL reserved user bits @@ -45,7 +55,7 @@ package top_racl_pkg; logic unused_rsvd_bits; unused_rsvd_bits = ^{rsvd}; - return racl_role_t'(rsvd[11:8]); + return racl_role_t'(rsvd[${racl_config['role_bit_msb']}:${racl_config['role_bit_lsb']}]); endfunction // Extract CTN UID bits from the TLUL reserved user bits @@ -54,7 +64,7 @@ package top_racl_pkg; logic unused_rsvd_bits; unused_rsvd_bits = ^{rsvd}; - return ctn_uid_t'(rsvd[7:0]); + return ctn_uid_t'(rsvd[${racl_config['ctn_uid_bit_msb']}:${racl_config['ctn_uid_bit_lsb']}]); endfunction % for racl_group, policies in racl_config['policies'].items(): @@ -68,15 +78,27 @@ package top_racl_pkg; * Policy ${policy['name']} allowed READ roles: * ${', '.join(policy['allowed_wr'])} */ - parameter racl_policy_t RACL_POLICY_${prefix}${policy['name'].upper()}_RD_DEFAULT = 16'h${f"{policy['rd_default']:x}"}; + parameter racl_role_vec_t RACL_POLICY_${prefix}${policy['name'].upper()}_RD_DEFAULT = ${racl_role_vec_len}'h${f"{policy['rd_default']:x}"}; /** * Policy ${policy['name']} allowed WRITE roles: * ${', '.join(policy['allowed_wr'])} */ - parameter racl_policy_t RACL_POLICY_${prefix}${policy['name'].upper()}_WR_DEFAULT = 16'h${f"{policy['wr_default']:x}"}; + parameter racl_role_vec_t RACL_POLICY_${prefix}${policy['name'].upper()}_WR_DEFAULT = ${racl_role_vec_len}'h${f"{policy['wr_default']:x}"}; % endfor % endfor +% for m in topcfg['module']: + % if 'racl_mappings' in m: + % for if_name, mapping in m['racl_mappings'].items(): + /** + * Policy selection vector for ${m["name"]} + */ +<% if_suffix = f"_{if_name.upper()}" if if_name else "" %>\ + parameter int unsigned RACL_POLICY_SEL_${m["name"].upper()}${if_suffix} [${len(mapping)}] = '{${", ".join(map(str, reversed(mapping.values())))}}; + + % endfor + % endif +% endfor endpackage diff --git a/util/topgen/validate.py b/util/topgen/validate.py index c48f7a9403a8c..f0ddfd82047f1 100644 --- a/util/topgen/validate.py +++ b/util/topgen/validate.py @@ -221,6 +221,10 @@ 'template_type': ['s', 'Base template type of ipgen IPs'], 'racl_group': ['s', 'Only valid for racl_ctrl IPs. Defines the RACL group this control IP is ' 'associated to'], + 'racl_mappings': ['g', 'dict that maps an interface to its associated RACL mapping'], + 'racl_mapping': ['s', 'A special case of racl_mappings. If specified, this is taken to ' + 'represent a dict that associates all interfaces with the give mapping. ' + 'It is an error to specify both this and racl_mappings.'], } module_added = {