diff --git a/hw/ip/aes/data/aes.rdl b/hw/ip/aes/data/aes.rdl new file mode 100644 index 00000000000000..24cb115afa37a0 --- /dev/null +++ b/hw/ip/aes/data/aes.rdl @@ -0,0 +1,320 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +addrmap aes { + reg { + field { + sw = w; + onwrite = woclr; + desc = "Initial Key Registers Share 0. + + The actual initial key corresponds to Initial Key Registers + Share 0 XORed with Initial Key Registers Share 1. Loaded into + the internal Full Key register upon starting + encryption/decryption of the next block. All key registers + (Share 0 and Share 1) must be written at least once when the + key is changed, regardless of key length (write random data + for unused bits). The order in which the registers are + updated does not matter. Can only be updated when the AES + unit is idle. If the AES unit is non-idle, writes to these + registers are ignored. + Upon reset, these registers are cleared with pseudo-random data."; + } KEY_SHARE0[31:0]; + } KEY_SHARE0[8] @ 0x04 += 0x20; + reg0 { + field { + sw = w; + onwrite = woclr; + desc = "Initial Key Registers Share 1. + + The actual initial key corresponds to Initial Key Registers + Share 0 XORed with Initial Key Registers Share 1. Loaded into + the internal Full Key register upon starting + encryption/decryption of the next block. All key registers + (Share 0 and Share 1) must be written at least once when the + key is changed, regardless of key length (write random data + for unused bits). The order in which the registers are + updated does not matter. Can only be updated when the AES + unit is idle. If the AES unit is non-idle, writes to these + registers are ignored. Upon reset, these registers are + cleared with pseudo-random data."; + } KEY_SHARE1[31:0]; + } KEY_SHARE1[8] @ 0x24 += 0x20; + reg { + field { + sw = rw; + onwrite = woclr; + desc = "Initialization Vector Registers. + + The initialization vector (IV) or initial counter value must + be written to these registers when starting a new message in + CBC or CTR mode (see Control Register), respectively. In CBC + and CTR modes, the AES unit does not start + encryption/decryption with a partially updated IV. Each + register has to be written at least once. The order in which + the registers are written does not matter. If the AES unit is + non-idle, writes to these registers are ignored. Whenever + starting a new message, the corresponding IV value must be + provided by the processor. Once started, the AES unit + automatically updates the contents of these registers. In ECB + mode, the IV registers are not used and do not need to be + configured. Upon reset, these registers are cleared with + pseudo-random data."; + } IV[31:0]; + } IV[4] @ 0x44 += 0x10; + reg { + field { + sw = w; + onwrite = woclr; + desc = "Input Data Registers. + + If MANUAL_OPERATION=0 (see Control Register), the AES unit + automatically starts encryption/decryption after all Input + Data registers have been written. Each register has to be + written at least once. The order in which the registers are + written does not matter. Loaded into the internal State + register upon starting encryption/decryption of the next + block. After that, the processor can update the Input Data + registers (See INPUT_READY field of Status Register). Upon + reset, these registers are cleared with pseudo-random data."; + } DATA_IN[31:0]; + } DATA_IN[4] @ 0x54 += 0x10; + reg { + field { + sw = r; + desc = "Output Data Register. + + Holds the output data produced by the AES unit during the last + encryption/decryption operation. If MANUAL_OPERATION=0 (see + Control Register), the AES unit is stalled when the previous + output data has not yet been read and is about to be + overwritten. Each register has to be read at least once. The + order in which the registers are read does not matter. Upon + reset, these registers are cleared with pseudo-random data."; + } DATA_OUT[31:0]; + } DATA_OUT[4] @ 0x64 += 0x10; + reg { + field { + sw = rw; + onwrite = woclr; + desc = "2-bit one-hot field to select the operation of AES + unit. Invalid input values, i.e., values with multiple + bits set and value 2'b00, are mapped to AES_ENC (2'b01)."; + } OPERATION[1:0]; + field { + sw = rw; + onwrite = woclr; + desc = "6-bit one-hot field to select AES block cipher + mode. Invalid input values, i.e., values with multiple + bits set and value 6'b00_0000, are mapped to AES_NONE + (6'b11_1111)."; + } MODE[7:2]; + field { + sw = rw; + onwrite = woclr; + desc = "3-bit one-hot field to select AES key length. + Invalid input values, i.e., values with multiple bits set, + value 3'b000, and value 3'b010 in case 192-bit keys are + not supported (because disabled at compile time) are + mapped to AES_256 (3'b100)."; + } KEY_LEN[10:8]; + field { + sw = rw; + onwrite = woclr; + desc = "Controls whether the AES unit uses the key + provided by the key manager via key sideload interface (1) + or the key provided by software via Initial Key Registers + KEY_SHARE1_0 - KEY_SHARE1_7 (0)."; + } SIDELOAD[11:11]; + field { + sw = rw; + onwrite = woclr; + desc = "3-bit one-hot field to control the reseeding rate + of the internal pseudo-random number generator (PRNG) used + for masking. Invalid input values, i.e., values with + multiple bits set and value 3'b000 are mapped to the + highest reseeding rate PER_1 (3'b001)."; + } PRNG_RESEED_RATE[14:12]; + field { + sw = rw; + onwrite = woclr; + desc = "Controls whether the AES unit is operated in + normal/automatic mode (0) or fully manual mode (1). In + automatic mode (0), the AES unit automatically i) starts + to encrypt/decrypt when it receives new input data, and + ii) stalls during the last encryption/decryption cycle if + the previous output data has not yet been read. This is + the most efficient mode to operate in. Note that the + corresponding status tracking is automatically cleared + upon a write to the Control Register. In manual mode (1), + the AES unit i) only starts to encrypt/decrypt after + receiving a start trigger (see Trigger Register), and ii) + overwrites previous output data irrespective of whether it + has been read out or not. This mode is useful if software needs full + control over the AES unit."; + } MANUAL_OPERATION[15:15]; + } CTRL_SHADOWED @ 0x74; + reg { + field { + sw = rw; + onwrite = woclr; + desc = "Controls whether providing a new key triggers the reseeding + of internal pseudo-random number generators used for clearing and + masking (1) or not (0)."; + } KEY_TOUCH_FORCES_RESEED[0:0]; + field { + sw = rw; + onwrite = woclr; + desc = "Allow the internal masking PRNG to advance (0) or + force its internal state (1) leading to constant masks. + Setting all masks to constant value can be useful when + performing SCA. To completely disable the masking, the + second key share (KEY_SHARE1_0 - KEY_SHARE1_7) must be + zero as well. In addition, a special seed needs to be + loaded into the masking PRNG using the EDN interface. + Only applicable if both the Masking parameter and the + SecAllowForcingMasks parameter are set to one."; + } FORCE_MASKS[1:1]; + } CTRL_AUX_SHADOWED @ 0x78; + reg { + field { + sw = rw; + onwrite = woclr; + desc = "Auxiliary Control Register configuration enable + bit. If this is cleared to 0, the Auxiliary Control + Register cannot be written anymore."; + } CTRL_AUX_REGWEN[0:0]; + } CTRL_AUX_REGWEN @ 0x7c; + reg { + field { + sw = w; + onwrite = woclr; + desc = "Keep AES unit paused (0) or trigger the + encryption/decryption of one data block (1). This trigger + is cleared to `0` if MANUAL_OPERATION=0 or if MODE=AES_NONE + (see Control Register)."; + } START[0:0]; + field { + sw = w; + onwrite = woclr; + desc = "Keep current values in Initial Key, internal Full + Key and Decryption Key registers, IV registers and Input + Data registers (0) or clear all those registers with + pseudo-random data (1)."; + } KEY_IV_DATA_IN_CLEAR[1:1]; + field { + sw = w; + onwrite = woclr; + desc = "Keep current values in Output Data registers (0) or + clear those registers with pseudo-random data (1)."; + } DATA_OUT_CLEAR[2:2]; + field { + sw = w; + onwrite = woclr; + desc = "Keep continuing with the current states of the + internal pseudo-random number generators used for register + clearing and masking (0) or perform a reseed of the internal + states from the connected entropy source (1). If the + KEY_TOUCH_FORCES_RESEED bit in the Auxiliary Control + Register is set to one, this trigger will automatically get + set after providing a new initial key."; + } PRNG_RESEED[3:3]; + } TRIGGER @ 0x80; + reg { + field { + sw = r; + desc = "The AES unit is idle (1) or busy (0). This flag + is `0` if one of the following operations is currently + running: i) encryption/decryption, ii) register clearing or + iii) PRNG reseeding. This flag is also `0` if an + encryption/decryption is running but the AES unit is + stalled."; + } IDLE[0:0]; + field { + sw = r; + desc = "The AES unit is not stalled (0) or stalled (1) + because there is previous output data that must be read by + the processor before the AES unit can overwrite this data. + This flag is not meaningful if MANUAL_OPERATION=1 (see + Control Register)."; + } STALL[1:1]; + field { + sw = r; + desc = "All previous output data has been fully read by + the processor (0) or at least one previous output data block + has been lost (1). It has been overwritten by the AES unit + before the processor could fully read it. Once set to `1`, + this flag remains set until AES operation is restarted by + re-writing the Control Register. The primary use of this + flag is for design verification. This flag is not + meaningful if MANUAL_OPERATION=0 (see Control Register)."; + } OUTPUT_LOST[2:2]; + field { + sw = r; + desc = "The AES unit has no valid output (0) or has valid output data (1)."; + } OUTPUT_VALID[3:3]; + field { + sw = r; + desc = "The AES unit is ready (1) or not ready (0) to + receive new data input via the DATA_IN registers. If the + present values in the DATA_IN registers have not yet been + loaded into the module this flag is `0` (not ready)."; + } INPUT_READY[4:4]; + field { + sw = r; + desc = "An update error has not occurred (0) or has + occurred (1) in the shadowed Control Register. AES + operation needs to be restarted by re-writing the Control + Register."; + } ALERT_RECOV_CTRL_UPDATE_ERR[5:5]; + field { + sw = r; + desc = "No fatal fault has occurred inside the AES unit + (0). A fatal fault has occurred and the AES unit needs to + be reset (1). Examples for fatal faults include i) storage + errors in the Control Register, ii) if any internal FSM + enters an invalid state, iii) if any sparsely encoded signal + takes on an invalid value, iv) errors in the internal round + counter, v) escalations triggered by the life cycle + controller, and vi) fatal integrity failures on the TL-UL bus."; + } ALERT_FATAL_FAULT[6:6]; + } STATUS @ 0x84; + reg { + field { + sw = rw; + onwrite = woclr; + desc = "6-bit one-hot field to select the phase of the + Galois/Counter Mode (GCM) of operation. Invalid input + values, i.e., values with multiple bits set and value + 6'b00_0000, are mapped to GCM_INIT (6'b00_0001). In case + support for GCM has been disabled at compile time, this + field is not writable and always reads as GCM_INIT + (6'b00_0001)."; + } PHASE[5:0]; + field { + sw = rw; + onwrite = woclr; + desc = "Number of valid bytes of the current input block. + Only the last block in the GCM_AAD and GCM_TEXT phases are + expected to have not all bytes marked as valid. For all + other blocks, the number of valid bytes should be set to 16. + Invalid input values, i.e., the value 5'b0_0000, and all + other values different from 5'b1_0000 in case GCM is not + supported (because disabled at compile time) are mapped to + 5'b1_0000."; + } NUM_VALID_BYTES[10:6]; + } CTRL_GCM_SHADOWED @ 0x88; + reg { + field { + sw = r; + desc = "Name register."; + } NAME[31:0]; + } NAME[2] @ 0x100 += 0x8; + reg { + field { + sw = r; + desc = "Version register."; + } VERSION[31:0]; + } VERSION[2] @ 0x108 += 0x8; + }; diff --git a/hw/ip/aes/pre_dv/aes_tlul_shim_tb/README.md b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/README.md new file mode 100644 index 00000000000000..a8b399cd5ed3ca --- /dev/null +++ b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/README.md @@ -0,0 +1,29 @@ +TLUL/Shim Verilator Testbench +======================= + +This directory contains the Verilator testbench for the TLUL shim adapter that is attached +to the AES IP block, run in the GCM mode. + + +How to build and run the example +-------------------------------- + +To build the testbench, execute from the OpenTitan top level: + +```sh +fusesoc --cores-root=. --verbose run --setup --build lowrisc:dv_verilator:aes_tlul_shim_tb +``` +To execute the obtained Verilator binary and with trace generation, run: + +```sh +./build/lowrisc_dv_verilator_aes_tlul_shim_tb_0/default-verilator/Vaes_tlul_shim_tb --trace +``` + +Details of the testbench +------------------------ + +- cpp/aes\_tlul\_shim\_tb.cc: contains main function and instantiation of SimCtrl +- rtl/aes\_tlul\_shim\_tb.sv: contains the testbench logic +- rtl/aes\_tlul\_delayer\_tb.sv: contains an optional delayer module to artificially induce random delays between the Shim and the TLUL bus. +- rtl/aes\_tlul\_shim\_tb_reqs.sv: contains requests (stimuli) that are fed to the testbench. +- rtl/aes\_tlul\_shim\_tb_pkg.sv: contains common parameters and functions. diff --git a/hw/ip/aes/pre_dv/aes_tlul_shim_tb/aes_tlul_shim_tb.core b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/aes_tlul_shim_tb.core new file mode 100644 index 00000000000000..6d53ba191d8760 --- /dev/null +++ b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/aes_tlul_shim_tb.core @@ -0,0 +1,56 @@ +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:dv_verilator:aes_tlul_shim_tb" +description: "AES TLUL Shim TB" +filesets: + files_rtl: + depend: + - lowrisc:ip:aes + - lowrisc:dv:aes_model_dpi + - lowrisc:tlul:adapter_shim + files: + - rtl/aes_tlul_shim_tb_pkg.sv + - rtl/aes_tlul_shim_tb_reqs.sv + - rtl/aes_tlul_shim_delayer.sv + - rtl/aes_tlul_shim_tb.sv + file_type: systemVerilogSource + + files_dv_verilator: + depend: + - lowrisc:dv_verilator:simutil_verilator + + files: + - cpp/aes_tlul_shim_tb.cc + file_type: cppSource + +targets: + default: + default_tool: verilator + filesets: + - files_rtl + - files_dv_verilator + toplevel: aes_tlul_shim_tb + tools: + verilator: + mode: cc + verilator_options: +# Disabling tracing reduces compile times by multiple times, but doesn't have a +# huge influence on runtime performance. (Based on early observations.) + - '--trace' + - '--trace-fst' # this requires -DVM_TRACE_FMT_FST in CFLAGS below! + - '--trace-structs' + - '--trace-params' + - '--trace-max-array 1024' +# compiler flags +# +# -O +# Optimization levels have a large impact on the runtime performance of the +# simulation model. -O2 and -O3 are pretty similar, -Os is slower than -O2/-O3 + - '-CFLAGS "-std=c++11 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=aes_tlul_shim_tb -g -O0"' + - '-LDFLAGS "-pthread -lutil -lelf"' + - "-Wall" + # XXX: Cleanup all warnings and remove this option + # (or make it more fine-grained at least) + - "-Wno-fatal" diff --git a/hw/ip/aes/pre_dv/aes_tlul_shim_tb/cpp/aes_tlul_shim_tb.cc b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/cpp/aes_tlul_shim_tb.cc new file mode 100644 index 00000000000000..eacce2b6b84c18 --- /dev/null +++ b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/cpp/aes_tlul_shim_tb.cc @@ -0,0 +1,67 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +#include "Vaes_tlul_shim_tb.h" +#include "sim_ctrl_extension.h" +#include "verilated_toplevel.h" +#include "verilator_sim_ctrl.h" + +class AesTlulShimTb : public SimCtrlExtension { + using SimCtrlExtension::SimCtrlExtension; + + public: + AesTlulShimTb(aes_tlul_shim_tb *top); + + void OnClock(unsigned long sim_time); + + private: + aes_tlul_shim_tb *top_; +}; + +// Constructor: +// - Set up top_ ptr +AesTlulShimTb::AesTlulShimTb(aes_tlul_shim_tb *top) + : SimCtrlExtension{}, top_(top) {} + +// Function called once every clock cycle from SimCtrl +void AesTlulShimTb::OnClock(unsigned long sim_time) { + if (top_->test_done_o) { + VerilatorSimCtrl::GetInstance().RequestStop(top_->test_passed_o); + } +} + +int main(int argc, char **argv) { + int ret_code; + + // Init verilog instance + aes_tlul_shim_tb top; + + // Init sim + VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance(); + simctrl.SetTop(&top, &top.clk_i, &top.rst_ni, + VerilatorSimCtrlFlags::ResetPolarityNegative); + + // Create and register VerilatorSimCtrl extension + AesTlulShimTb aes_tlul_shim_tb(&top); + simctrl.RegisterExtension(&aes_tlul_shim_tb); + + std::cout << "Simulation of AES TLUL Shim" << std::endl + << "=============================" << std::endl + << std::endl; + + // Get pass / fail from Verilator + ret_code = simctrl.Exec(argc, argv).first; + + if (ret_code == 0) { + std::cout << "Simulation passed!" << std::endl; + } else { + std::cout << "Simulation failed!" << std::endl; + } + + return ret_code; +} diff --git a/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_delayer.sv b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_delayer.sv new file mode 100644 index 00000000000000..6fcc31d7011473 --- /dev/null +++ b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_delayer.sv @@ -0,0 +1,104 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module aes_tlul_shim_delayer + import tlul_pkg::*; +#( + parameter bit DelayerEnable = 1 +) ( + input logic clk_i, + input logic rst_ni, + + input tl_h2d_t tl_h2d_i, + input tl_d2h_t tl_d2h_i, + + output tl_h2d_t tl_h2d_delayed_o, + output tl_d2h_t tl_d2h_delayed_o +); + + logic tlul_pass; + + generate + if (DelayerEnable) begin : gen_delay_logic + logic [3:0] delay_cntr_d, delay_cntr_q; + + logic lfsr_en; + logic [3:0] lfsr_out; + + // 8-bit LFSR with a period of 2^8-1 and 4-bit output. + prim_lfsr #( + .LfsrDw ( 8 ), + .StateOutDw ( 4 ) + ) u_prim_lfsr ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .seed_en_i ( '0 ), + .seed_i ( '0 ), + .lfsr_en_i ( lfsr_en ), + .entropy_i ( '0 ), + .state_o ( lfsr_out ) + ); + + // The delay counter is active when `tl_d2h_i.d_valid`. Until the max value is reached + // `tl_h2d_i.d_ready` and `tl_d2h_i.d_valid` are pulled down. The counter remains at the max + // value until the device-to-host response is acknowledged. This is robust even in the case + // when the `tl_d2h_i.d_valid` is deasserted without having received a host-side + // acknowledgement. + assign tlul_pass = delay_cntr_q == lfsr_out; + assign lfsr_en = tlul_pass & tl_h2d_i.d_ready & tl_d2h_i.d_valid; + + always_comb begin + delay_cntr_d = delay_cntr_q; + if (lfsr_en) begin + delay_cntr_d = 0; + end else if (tl_d2h_i.d_valid) begin + delay_cntr_d = delay_cntr_q + 1'b1; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + delay_cntr_q <= 0; + end else begin + delay_cntr_q <= delay_cntr_d; + end + end + end else begin : gen_no_delay + assign tlul_pass = 1'b1; + + logic unused_signals; + assign unused_signals = ^{clk_i, rst_ni}; + end + endgenerate + + + // A delay is created by manually pulling down `d_valid` and `d_ready` + // which offsets the TLUL response. + assign tl_h2d_delayed_o = '{ + a_valid: tl_h2d_i.a_valid, + a_opcode: tl_h2d_i.a_opcode, + a_size: tl_h2d_i.a_size, + a_mask: tl_h2d_i.a_mask, + a_source: tl_h2d_i.a_source, + a_address: tl_h2d_i.a_address, + a_data: tl_h2d_i.a_data, + a_user: tl_h2d_i.a_user, + d_ready: tl_h2d_i.d_ready & tlul_pass, + a_param: tl_h2d_i.a_param + }; + + assign tl_d2h_delayed_o = '{ + d_valid: tl_d2h_i.d_valid & tlul_pass, + d_opcode: tl_d2h_i.d_opcode, + d_param: tl_d2h_i.d_param, + d_size: tl_d2h_i.d_size, + d_source: tl_d2h_i.d_source, + d_sink: tl_d2h_i.d_sink, + d_data: tl_d2h_i.d_data, + d_user: tl_d2h_i.d_user, + d_error: tl_d2h_i.d_error, + a_ready: tl_d2h_i.a_ready + }; + +endmodule diff --git a/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_tb.sv b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_tb.sv new file mode 100644 index 00000000000000..0413298adddb33 --- /dev/null +++ b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_tb.sv @@ -0,0 +1,247 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module aes_tlul_shim_tb + import tlul_pkg::*; + import aes_pkg::*; + import aes_reg_pkg::*; + import aes_tlul_shim_tb_pkg::*; + +#( + localparam bit AES192Enable = 1, + localparam bit AESGCMEnable = 1, + localparam bit SecMasking = 1, + localparam sbox_impl_e SecSBoxImpl = SBoxImplDom, + localparam int unsigned SecStartTriggerDelay = 0, + localparam bit SecAllowForcingMasks = 0, + localparam bit SecSkipPRNGReseeding = 0, + localparam bit EnableDataIntgGen = 1, + localparam bit EnableRspDataIntgCheck = 1, + localparam bit DelayerEnable = 0 + ) ( + input logic clk_i, + input logic rst_ni, + + output logic test_done_o, + output logic test_passed_o +); + + tlul_pkg::tl_h2d_t tl_h2d; + tlul_pkg::tl_d2h_t tl_d2h; + + tlul_pkg::tl_h2d_t tl_h2d_delayed; + tlul_pkg::tl_d2h_t tl_d2h_delayed; + + // Use a counter to provide some entropy for visual inspection. + logic [31:0] entropy_q; + logic edn_req; + always_ff @(posedge clk_i or negedge rst_ni) begin : reg_entropy + if (!rst_ni) begin + entropy_q <= 32'h12345678; + end else if (edn_req) begin + entropy_q <= entropy_q + 32'h1; + end + end + + logic shim_done; + logic shim_pop; + shim_request_t shim_req; + + logic shim_hld; + logic shim_error; + logic [top_pkg::TL_DW-1:0] shim_rdata; + + logic error; + assign error = shim_error | dpi_error; + + logic test_passed_q; + logic test_done_q; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + test_passed_q <= 1'b1; + test_done_q <= 1'b0; + end else if (shim_done || error) begin + test_passed_q <= ~error; + test_done_q <= 1'b1; + end + end + assign test_done_o = test_done_q; + assign test_passed_o = test_passed_q; + + // The AES block is run in automatic mode, which means the status register has to be polled in + // order to check whether an operation has finished. A convenient side-effect of this is that it + // will generate a large number of TLUL requests, which in combination with the delayer serves as + // a good stress test of the shim. + logic poll; + + // Reads of the AES status registers are repeated (without popping a new request from the stack) + // until it matches the `shim_req.mask`. Such a stall can occur when the `ÌDLE` bit needs to be + // asserted before the computation can advance. + assign poll = ~shim_hld + & (shim_req.addr == 32'(AES_STATUS_OFFSET)) + & ~|(shim_rdata & shim_req.mask); + assign shim_pop = ~shim_hld & ~poll; + + aes_tlul_shim_tb_reqs u_aes_tlul_shim_tb_reqs ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .pop_req_i ( shim_pop ), + .req_o ( shim_req ), + .done_o ( shim_done ) + ); + + tlul_adapter_shim #( + .EnableDataIntgGen ( EnableDataIntgGen ), + .EnableRspDataIntgCheck ( EnableRspDataIntgCheck ) + ) u_tlul_adapter_shim ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tl_i ( tl_d2h_delayed ), + .tl_o ( tl_h2d ), + .dv_i ( shim_req.dv ), + .addr_i ( shim_req.addr ), + .write_i ( shim_req.write ), + .wdata_i ( shim_req.wdata ), + .wstrb_i ( shim_req.wstrb ), + .size_i ( shim_req.size ), + .hld_o ( shim_hld ), + .rdata_o ( shim_rdata ), + .error_o ( shim_error ), + .last_i ( shim_req.last ), + .user_i ( shim_req.user ), + .id_i ( shim_req.id ) + ); + + aes_tlul_shim_delayer #( + .DelayerEnable ( DelayerEnable ) + ) u_aes_tlul_shim_delayer ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tl_h2d_i ( tl_h2d ), + .tl_d2h_i ( tl_d2h ), + .tl_h2d_delayed_o ( tl_h2d_delayed ), + .tl_d2h_delayed_o ( tl_d2h_delayed ) + ); + + aes #( + .AES192Enable ( AES192Enable ), + .AESGCMEnable ( AESGCMEnable ), + .SecMasking ( SecMasking ), + .SecSBoxImpl ( SecSBoxImpl ), + .SecStartTriggerDelay ( SecStartTriggerDelay ), + .SecAllowForcingMasks ( SecAllowForcingMasks ), + .SecSkipPRNGReseeding ( SecSkipPRNGReseeding ) + ) u_aes ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .rst_shadowed_ni ( rst_ni ), + .idle_o ( ), + .lc_escalate_en_i ( lc_ctrl_pkg::Off ), + .clk_edn_i ( clk_i ), + .rst_edn_ni ( rst_ni ), + .edn_o ( edn_req ), + .edn_i ( {edn_req, 1'b1, entropy_q} ), + .keymgr_key_i ( ), + .tl_i ( tl_h2d_delayed ), + .tl_o ( tl_d2h ), + .alert_rx_i ( alert_rx ), + .alert_tx_o ( alert_tx ) + ); + + ////////////////////////////////////// + // AES-GCM Correctness Verification // + ////////////////////////////////////// + + // This is a sanity check verifying whether the AES-128-GCM block has correctly + // computed the authentication tag for a single-block message + // with a zero key and IV inputs, which is congruent to test case #2 in + // + // https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf + // + // Instead of hardcoding the model tag, this testbench calls into the `c_dpi_aes` + // reference implementation. + + // The ciphertext and authentication tag parts are compared in the same cycle + // after a successful read of the AES data output register. + + logic out_cntr_en; + assign out_cntr_en = ~shim_hld && (shim_req.addr == 32'(AES_DATA_OUT_0_OFFSET) || + shim_req.addr == 32'(AES_DATA_OUT_1_OFFSET) || + shim_req.addr == 32'(AES_DATA_OUT_2_OFFSET) || + shim_req.addr == 32'(AES_DATA_OUT_3_OFFSET)) ? 1'b1 : 1'b0; + + int out_cntr_d, out_cntr_q; + always_comb begin + out_cntr_d = out_cntr_q; + if (out_cntr_en) begin + out_cntr_d = out_cntr_q + 1; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + out_cntr_q <= 0; + end else begin + out_cntr_q <= out_cntr_d; + end + end + + localparam logic [255:0] c_dpi_key = '{default: '0}; + localparam logic [127:0] c_dpi_iv = '{default: '0}; + localparam logic [7:0] c_dpi_pt [16] = '{default: '0}; + + logic [31:0][7:0] c_dpi_model_q; + logic [7:0] c_dpi_ct [16]; + logic [3:0][3:0][7:0] c_dpi_tag; + + always_comb begin + aes_model_dpi_pkg::c_dpi_aes_crypt_message( + 1'b1, // Reference Implementation: OpenSSL/BoringSSL + 1'b0, // Operation: Encryption + AES_GCM , // Mode: GCM + c_dpi_iv, // IV + AES_128, // Key Length: 128 + c_dpi_key, // Key + c_dpi_pt, // Data Input + '0, // AD Input: null + '0, // Tag Input: null + c_dpi_ct, // CT Output + c_dpi_tag // Tag Output + ); + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + c_dpi_model_q[31:16] <= aes_transpose(c_dpi_tag); + for (int i = 0; i < 16; i++) begin + c_dpi_model_q[i] <= c_dpi_ct[i]; + end + end else if (out_cntr_en) begin + for (int i = 0; i < 32; i++) begin + // Rotate `c_dpi` output. + c_dpi_model_q[i] <= c_dpi_model_q[(i+4)%32]; + end + end + end + + logic dpi_error; + assign dpi_error = out_cntr_en && c_dpi_model_q[3:0] != shim_rdata ? 1'b1 : 1'b0; + + // We do not care about alerts in this testbench. Set them to constant values. + prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx; + prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx; + assign alert_rx[0].ping_p = 1'b0; + assign alert_rx[0].ping_n = 1'b1; + assign alert_rx[0].ack_p = 1'b0; + assign alert_rx[0].ack_n = 1'b1; + assign alert_rx[1].ping_p = 1'b0; + assign alert_rx[1].ping_n = 1'b1; + assign alert_rx[1].ack_p = 1'b0; + assign alert_rx[1].ack_n = 1'b1; + + // Tie off unused signals. + logic unused_signals; + assign unused_signals = ^{alert_tx}; + +endmodule diff --git a/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_tb_pkg.sv b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_tb_pkg.sv new file mode 100644 index 00000000000000..ee40ebdb11a85f --- /dev/null +++ b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_tb_pkg.sv @@ -0,0 +1,79 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package aes_tlul_shim_tb_pkg; + + import tlul_pkg::*; + + parameter bit AES_MANUAL_OPERATION = 0; + parameter bit AES_SIDELOAD = 0; + parameter int AES_GCM_NUM_VALID_BYTES = 16; + + parameter int AES_CTRL_OPERATION_OFFSET = 0; + parameter int AES_CTRL_MODE_OFFSET = 2; + parameter int AES_CTRL_KEY_LEN_OFFSET = 8; + parameter int AES_CTRL_SIDELOAD_OFFSET = 11; + parameter int AES_CTRL_PRNG_RESEED_RATE_OFFSET = 12; + parameter int AES_CTRL_MANUAL_OPERATION_OFFSET = 15; + + parameter int AES_CTRL_GCM_PHASE_OFFSET = 0; + parameter int AES_CTRL_GCM_NUM_VALID_BYTES_OFFSET = 6; + + parameter int AES_STATUS_IDLE_OFFSET = 0; + parameter int AES_STATUS_STALL_OFFSET = 1; + parameter int AES_STATUS_OUTPUT_LOST_OFFSET = 2; + parameter int AES_STATUS_OUTPUT_VALID_OFFSET = 3; + parameter int AES_STATUS_INPUT_READY_OFFSET = 4; + + typedef struct packed { + logic dv; + logic [top_pkg::TL_AW-1:0] addr; + logic write; + logic [top_pkg::TL_DW-1:0] wdata; + logic [top_pkg::TL_DBW-1:0] wstrb; + logic [2:0] size; + logic last; + logic [31:0] user; + logic [top_pkg::TL_AIW-1:0] id; + + // Internal signal to verify status bits. + logic [top_pkg::TL_DW-1:0] mask; + } shim_request_t; + + function automatic shim_request_t write_request (logic [7:0] addr, + logic [top_pkg::TL_DW-1:0] wdata); + shim_request_t req = '{ + dv: 1'b1, + addr: {24'b0, addr}, + write: 1'b1, + wdata: wdata, + wstrb: top_pkg::TL_DBW'(4'b1111), + size: 3'b010, + last: 1'b0, + user: 32'(TL_A_USER_DEFAULT), + id: '{default: '0}, + mask: '{default: '0} + }; + return req; + endfunction + + function automatic shim_request_t read_request (logic [7:0] addr, + logic [top_pkg::TL_DW-1:0] mask = '0, + bit calyptra = 1'b0); + shim_request_t req = '{ + dv: 1'b1, + addr: {23'b0, calyptra, addr}, + write: 1'b0, + wdata: '{default: '0}, + wstrb: top_pkg::TL_DBW'(4'b1111), + size: 3'b010, + last: 1'b0, + user: 32'(TL_A_USER_DEFAULT), + id: '{default: '0}, + mask: mask + }; + return req; + endfunction + +endpackage diff --git a/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_tb_reqs.sv b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_tb_reqs.sv new file mode 100644 index 00000000000000..d4101f21c80836 --- /dev/null +++ b/hw/ip/aes/pre_dv/aes_tlul_shim_tb/rtl/aes_tlul_shim_tb_reqs.sv @@ -0,0 +1,159 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module aes_tlul_shim_tb_reqs + import aes_pkg::*; + import aes_reg_pkg::*; + import tlul_pkg::*; + import aes_tlul_shim_tb_pkg::*; +( + input logic clk_i, + input logic rst_ni, + + input logic pop_req_i, + + output shim_request_t req_o, + output logic done_o +); + + localparam int NumRequests = 54; + shim_request_t requests[NumRequests]; + + initial begin + requests = '{ + // Check AES core is idle before writing the control registers. + read_request(AES_STATUS_OFFSET, 32'(1'b1) << AES_STATUS_IDLE_OFFSET), + + // Config AES core. Config GCM in `INIT` mode. + write_request( + AES_CTRL_SHADOWED_OFFSET, + 32'(AES_MANUAL_OPERATION) << AES_CTRL_MANUAL_OPERATION_OFFSET | + 32'(PER_1) << AES_CTRL_PRNG_RESEED_RATE_OFFSET | + 32'(AES_SIDELOAD) << AES_CTRL_SIDELOAD_OFFSET | + 32'(AES_128) << AES_CTRL_KEY_LEN_OFFSET | + 32'(AES_GCM) << AES_CTRL_MODE_OFFSET | + 32'(AES_ENC) << AES_CTRL_OPERATION_OFFSET + ), + write_request( + AES_CTRL_SHADOWED_OFFSET, + 32'(AES_MANUAL_OPERATION) << AES_CTRL_MANUAL_OPERATION_OFFSET | + 32'(PER_1) << AES_CTRL_PRNG_RESEED_RATE_OFFSET | + 32'(AES_SIDELOAD) << AES_CTRL_SIDELOAD_OFFSET | + 32'(AES_128) << AES_CTRL_KEY_LEN_OFFSET | + 32'(AES_GCM) << AES_CTRL_MODE_OFFSET | + 32'(AES_ENC) << AES_CTRL_OPERATION_OFFSET + ), + write_request( + AES_CTRL_GCM_SHADOWED_OFFSET, + 32'(AES_GCM_NUM_VALID_BYTES) << AES_CTRL_GCM_NUM_VALID_BYTES_OFFSET | + 32'(GCM_INIT) << AES_CTRL_GCM_PHASE_OFFSET + ), + write_request( + AES_CTRL_GCM_SHADOWED_OFFSET, + 32'(AES_GCM_NUM_VALID_BYTES) << AES_CTRL_GCM_NUM_VALID_BYTES_OFFSET | + 32'(GCM_INIT) << AES_CTRL_GCM_PHASE_OFFSET + ), + read_request(AES_STATUS_OFFSET, 32'(1'b1) << AES_STATUS_IDLE_OFFSET), + + // Write key registers. + write_request(AES_KEY_SHARE0_0_OFFSET, 32'h03020100), + write_request(AES_KEY_SHARE0_1_OFFSET, 32'h07060504), + write_request(AES_KEY_SHARE0_2_OFFSET, 32'h0B0A0908), + write_request(AES_KEY_SHARE0_3_OFFSET, 32'h0F0E0D0C), + write_request(AES_KEY_SHARE0_4_OFFSET, 32'h13121110), + write_request(AES_KEY_SHARE0_5_OFFSET, 32'h17161514), + write_request(AES_KEY_SHARE0_6_OFFSET, 32'h1B1A1918), + write_request(AES_KEY_SHARE0_7_OFFSET, 32'h1F1E1D1C), + write_request(AES_KEY_SHARE1_0_OFFSET, 32'h03020100), + write_request(AES_KEY_SHARE1_1_OFFSET, 32'h07060504), + write_request(AES_KEY_SHARE1_2_OFFSET, 32'h0B0A0908), + write_request(AES_KEY_SHARE1_3_OFFSET, 32'h0F0E0D0C), + write_request(AES_KEY_SHARE1_4_OFFSET, 32'h13121110), + write_request(AES_KEY_SHARE1_5_OFFSET, 32'h17161514), + write_request(AES_KEY_SHARE1_6_OFFSET, 32'h1B1A1918), + write_request(AES_KEY_SHARE1_7_OFFSET, 32'h1F1E1D1C), + read_request(AES_STATUS_OFFSET, 32'(1'b1) << AES_STATUS_IDLE_OFFSET), + + // Write IV registers. + write_request(AES_IV_0_OFFSET, 32'h00000000), + write_request(AES_IV_1_OFFSET, 32'h00000000), + write_request(AES_IV_2_OFFSET, 32'h00000000), + write_request(AES_IV_3_OFFSET, 32'h00000000), + read_request(AES_STATUS_OFFSET, 32'(1'b1) << AES_STATUS_IDLE_OFFSET), + + // Config GCM in `TEXT` mode and write plaintext into the data registers. + write_request( + AES_CTRL_GCM_SHADOWED_OFFSET, + 32'(AES_GCM_NUM_VALID_BYTES) << AES_CTRL_GCM_NUM_VALID_BYTES_OFFSET | + 32'(GCM_TEXT) << AES_CTRL_GCM_PHASE_OFFSET + ), + write_request( + AES_CTRL_GCM_SHADOWED_OFFSET, + 32'(AES_GCM_NUM_VALID_BYTES) << AES_CTRL_GCM_NUM_VALID_BYTES_OFFSET | + 32'(GCM_TEXT) << AES_CTRL_GCM_PHASE_OFFSET + ), + read_request(AES_STATUS_OFFSET, 32'(1'b1) << AES_STATUS_IDLE_OFFSET), + + write_request(AES_DATA_IN_0_OFFSET, 32'h00000000), + write_request(AES_DATA_IN_1_OFFSET, 32'h00000000), + write_request(AES_DATA_IN_2_OFFSET, 32'h00000000), + write_request(AES_DATA_IN_3_OFFSET, 32'h00000000), + read_request(AES_STATUS_OFFSET, 32'(1'b1) << AES_STATUS_OUTPUT_VALID_OFFSET), + + // Read out ciphertext + read_request(AES_DATA_OUT_0_OFFSET), + read_request(AES_DATA_OUT_1_OFFSET), + read_request(AES_DATA_OUT_2_OFFSET), + read_request(AES_DATA_OUT_3_OFFSET), + read_request(AES_STATUS_OFFSET, 32'(1'b1) << AES_STATUS_IDLE_OFFSET), + + // Config GCM in `TAG` mode and write len(ad) || len(pt) to trigger tag computation. + write_request( + AES_CTRL_GCM_SHADOWED_OFFSET, + 32'(AES_GCM_NUM_VALID_BYTES) << AES_CTRL_GCM_NUM_VALID_BYTES_OFFSET | + 32'(GCM_TAG) << AES_CTRL_GCM_PHASE_OFFSET + ), + write_request( + AES_CTRL_GCM_SHADOWED_OFFSET, + 32'(AES_GCM_NUM_VALID_BYTES) << AES_CTRL_GCM_NUM_VALID_BYTES_OFFSET | + 32'(GCM_TAG) << AES_CTRL_GCM_PHASE_OFFSET + ), + write_request(AES_DATA_IN_0_OFFSET, 32'h00000000), + write_request(AES_DATA_IN_1_OFFSET, 32'h00000000), + write_request(AES_DATA_IN_2_OFFSET, 32'h00000000), + write_request(AES_DATA_IN_3_OFFSET, 32'h80000000), // 1 Block + read_request(AES_STATUS_OFFSET, 32'(1'b1) << AES_STATUS_OUTPUT_VALID_OFFSET), + + // Read back the authentication tag. + read_request(AES_DATA_OUT_0_OFFSET), + read_request(AES_DATA_OUT_1_OFFSET), + read_request(AES_DATA_OUT_2_OFFSET), + read_request(AES_DATA_OUT_3_OFFSET), + + // Read Caliptra name and version registers. + read_request(8'h00,, 1'b1), + read_request(8'h04,, 1'b1) + }; + end + + int request_cntr_q; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + request_cntr_q <= 0; + end else if (pop_req_i) begin + request_cntr_q <= request_cntr_q + 1; + end + end + + always_comb begin + if (request_cntr_q < NumRequests) begin + req_o = requests[request_cntr_q]; + end else begin + req_o = '0; + end + end + + assign done_o = (request_cntr_q >= NumRequests) ? 1'b1 : 1'b0; + +endmodule diff --git a/hw/ip/tlul/adapter_shim.core b/hw/ip/tlul/adapter_shim.core new file mode 100644 index 00000000000000..9dc1e9b2a740be --- /dev/null +++ b/hw/ip/tlul/adapter_shim.core @@ -0,0 +1,64 @@ +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_shim:0.1" +description: "TL-UL/Shim interface adapter" + +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:secded + - lowrisc:tlul:common + - lowrisc:tlul:trans_intg + files: + - rtl/tlul_adapter_shim.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/tlul_adapter_shim.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/tlul_adapter_shim.waiver + file_type: waiver + + 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_shim + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/hw/ip/tlul/lint/tlul_adapter_shim.vlt b/hw/ip/tlul/lint/tlul_adapter_shim.vlt new file mode 100644 index 00000000000000..e3eb84558c3693 --- /dev/null +++ b/hw/ip/tlul/lint/tlul_adapter_shim.vlt @@ -0,0 +1,5 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// waiver file for tlul_adapter_shim diff --git a/hw/ip/tlul/lint/tlul_adapter_shim.waiver b/hw/ip/tlul/lint/tlul_adapter_shim.waiver new file mode 100644 index 00000000000000..694bccdee2fd8f --- /dev/null +++ b/hw/ip/tlul/lint/tlul_adapter_shim.waiver @@ -0,0 +1,5 @@ +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for TLUL Shim adapter diff --git a/hw/ip/tlul/rtl/tlul_adapter_shim.sv b/hw/ip/tlul/rtl/tlul_adapter_shim.sv new file mode 100644 index 00000000000000..c17170a8572878 --- /dev/null +++ b/hw/ip/tlul/rtl/tlul_adapter_shim.sv @@ -0,0 +1,142 @@ +// 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" + +module tlul_adapter_shim + import tlul_pkg::*; + import prim_mubi_pkg::mubi4_t; +#( + parameter bit [31:0] SHIM_NAME_0 = 32'hDEADBEEF, + parameter bit [31:0] SHIM_NAME_1 = 32'hCAFEBABE, + parameter bit [31:0] SHIM_VERSION_0 = 32'h00000001, + parameter bit [31:0] SHIM_VERSION_1 = 32'h00000000, + + parameter bit [11:0] SHIM_REGISTER_ADDRESS_OFFSET = 12'h100, + + parameter int ADDR_WIDTH = top_pkg::TL_AW, + parameter int DATA_WIDTH = top_pkg::TL_DW, + parameter int MASK_WIDTH = DATA_WIDTH >> 3, + parameter int USER_WIDTH = 32, + parameter int ID_WIDTH = top_pkg::TL_AIW, + + parameter bit EnableDataIntgGen = 1, + parameter bit EnableRspDataIntgCheck = 1 +) ( + input clk_i, + input rst_ni, + + output tl_h2d_t tl_o, + input tl_d2h_t tl_i, + + // Shim interface + input logic dv_i, + output logic hld_o, + input logic [ADDR_WIDTH-1:0] addr_i, + input logic write_i, + input logic [DATA_WIDTH-1:0] wdata_i, + input logic [MASK_WIDTH-1:0] wstrb_i, + input logic [2:0] size_i, + output logic [DATA_WIDTH-1:0] rdata_o, + output logic error_o, + // Optional signals + input logic last_i, + input logic [USER_WIDTH-1:0] user_i, + input logic [ID_WIDTH-1:0] id_i +); + + // Differentiate between two levels of acknowledgements: + // - `req_ack`: A host-to-device request is acknowledged by the device, + // meaning that the response is pending. + // - `resp_ack`: The device-to-host response is acknowledged. + logic pending_d, pending_q; + logic req_ack, resp_ack; + + assign req_ack = dv_i & tl_i.a_ready & tl_o.a_valid & ~internal_read; + assign resp_ack = dv_i & tl_o.d_ready & tl_i.d_valid & ~internal_read; + + always_comb begin + pending_d = pending_q; + if (req_ack) begin + pending_d = 1'b1; + end else if (resp_ack) begin + pending_d = 1'b0; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + pending_q <= 1'b0; + end else begin + pending_q <= pending_d; + end + end + + // Only make a new request (through `a_valid`) to the device when none are + // pending. If the integrity checks are enabled, the `a_user` fields will be + // set by the corresponding module (see `tlul_cmd_intg_gen`). + tlul_pkg::tl_h2d_t tl_o_pre; + assign tl_o_pre = '{ + a_valid: dv_i & ~pending_q & ~internal_read, + a_opcode: ~write_i ? Get : (&wstrb_i ? PutFullData : PutPartialData), + a_size: top_pkg::TL_SZW'(size_i), + a_mask: wstrb_i, + a_source: id_i, + a_address: addr_i, + a_data: wdata_i, + a_user: '{default: '0, instr_type: prim_mubi_pkg::MuBi4False}, + d_ready: 1'b1, + // unused + a_param: 3'h0 + }; + + logic internal_read; + assign internal_read = |(addr_i[11:0] & SHIM_REGISTER_ADDRESS_OFFSET); + + // Accesses to shim internal registers have no latency. Accesses to device registers are + // acknowledged with the TLUL d-channel handshake. + assign hld_o = !pending_q && internal_read ? 1'b0 : + pending_q && tl_o.d_ready && tl_i.d_valid ? 1'b0 : 1'b1; + + // A read request to an internal register is resolved in the same clock cycle. + always_comb begin + rdata_o = tl_i.d_data; + if (internal_read) begin + unique case (addr_i[3:0]) + 4'h0: rdata_o = SHIM_NAME_0; + 4'h4: rdata_o = SHIM_NAME_1; + 4'h8: rdata_o = SHIM_VERSION_0; + 4'hc: rdata_o = SHIM_VERSION_1; + default: rdata_o = tl_i.d_data; + endcase + end + end + + tlul_cmd_intg_gen #( + .EnableDataIntgGen (EnableDataIntgGen) + ) u_cmd_intg_gen ( + .tl_i ( tl_o_pre ), + .tl_o ( tl_o ) + ); + + logic intg_err_chk; + tlul_rsp_intg_chk #( + .EnableRspDataIntgCheck(EnableRspDataIntgCheck) + ) u_rsp_chk ( + .tl_i ( tl_i ), + .err_o ( intg_err_chk ) + ); + + assign error_o = tl_i.d_error | intg_err_chk; + + logic unused_signals; + assign unused_signals = ^{last_i, user_i, size_i[2]}; + + // Make sure the shim parameters are compatible with TLUL datapath widths. + `ASSERT_INIT(TlInvalidAddrWidth_A, ADDR_WIDTH == top_pkg::TL_AW) + `ASSERT_INIT(TlInvalidDataWidth_A, DATA_WIDTH == top_pkg::TL_DW) + `ASSERT_INIT(TlInvalidMaskWidth_A, MASK_WIDTH == top_pkg::TL_DBW) + `ASSERT_INIT(TlInvalidUserWidth_A, ID_WIDTH == top_pkg::TL_AIW) + +endmodule