diff --git a/.github/scripts/build_tests_matrix.py b/.github/scripts/build_tests_matrix.py new file mode 100644 index 000000000..a3bb3329e --- /dev/null +++ b/.github/scripts/build_tests_matrix.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +import yaml + +def main(): + + # Load and parse YAML description + file_name = "src/integration/stimulus/L0_regression.yml" + with open(file_name, "r") as fp: + yaml_root = yaml.safe_load(fp) + + # Get test list + content = yaml_root["contents"][0] + tests = content["tests"] + paths = tests["paths"] + + # Extract test names from paths + test_list = [] + for path in paths: + parts = path.split("/") + + for i, part in enumerate(parts): + if part == "test_suites" and i + 1 < len(parts): + test_list.append(parts[i+1]) + break + + # Output names + print(test_list) + +if __name__ == "__main__": + main() diff --git a/.github/workflows/build-test-verilator.yml b/.github/workflows/build-test-verilator.yml index b639a7f14..9f99ca7c1 100644 --- a/.github/workflows/build-test-verilator.yml +++ b/.github/workflows/build-test-verilator.yml @@ -9,23 +9,23 @@ on: workflow_dispatch: +env: + CARGO_INCREMENTAL: 0 + SCCACHE_VERSION: 0.3.3 + RISCV_VERSION: v12.1.0 + # TODO: To update to 5.006, clean up lint errors + VERILATOR_VERSION: v5.002 + PKG_CONFIG_PATH: /opt/verilator/share/pkgconfig + SCCACHE_GHA_CACHE_TO: sccache-verilator-10000 + SCCACHE_GHA_CACHE_FROM: sccache-verilator- + # Change this to a new random value if you suspect the cache is corrupted + SCCACHE_C_CUSTOM_CACHE_BUSTER: f3e6951f0c1d + jobs: - build_and_test: - name: Verilator Smoke Test + build_tools: + name: Build Tools runs-on: ubuntu-22.04 - env: - CARGO_INCREMENTAL: 0 - SCCACHE_VERSION: 0.3.3 - RISCV_VERSION: v12.1.0 - # TODO: To update to 5.006, clean up lint errors - VERILATOR_VERSION: v4.228 - PKG_CONFIG_PATH: /opt/verilator/share/pkgconfig - SCCACHE_GHA_CACHE_TO: sccache-verilator-10000 - SCCACHE_GHA_CACHE_FROM: sccache-verilator- - # Change this to a new random value if you suspect the cache is corrupted - SCCACHE_C_CUSTOM_CACHE_BUSTER: f3e6951f0c1d - steps: - uses: actions/checkout@v3 with: @@ -113,14 +113,85 @@ jobs: path: /opt/riscv key: riscv-${{ env.RISCV_VERSION }}-${{ env.SCCACHE_C_CUSTOM_CACHE_BUSTER }} + - name: Setup riscv path + run: | + echo /opt/riscv/bin >> $GITHUB_PATH + + build_matrix: + name: Build Smoke Test matrix + runs-on: ubuntu-22.04 + needs: build_tools + outputs: + test_names: ${{ steps.output-matrix.outputs.test_names }} + steps: + - uses: actions/checkout@v3 + - name: Install deps + run: | + sudo apt-get update -qy && sudo apt-get install -qy --no-install-recommends \ + python3-minimal python3-yaml + - name: Build matrix + id: output-matrix + run: | + echo "test_names=$(python3 .github/scripts/build_tests_matrix.py)" >> $GITHUB_OUTPUT + + build_and_test: + name: Verilator Smoke Test + runs-on: ubuntu-22.04 + needs: build_matrix + + strategy: + fail-fast: false + matrix: + test_name: ${{fromJSON(needs.build_matrix.outputs.test_names)}} + + steps: + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - name: Restore Cargo index + uses: actions/cache/restore@v3 + id: cargo_index_restore + with: + path: ~/.cargo/registry/index + key: cargo-index-${{ env.SCCACHE_C_CUSTOM_CACHE_BUSTER }}-${{ hashFiles('Cargo.lock') }} + + - name: Restore sccache binary + uses: actions/cache/restore@v3 + id: sccache_bin_restore + with: + path: ~/.cargo/bin/sccache + key: sccache-bin-${{ env.SCCACHE_VERSION }}-${{ env.SCCACHE_C_CUSTOM_CACHE_BUSTER }} + + - name: Restore verilator dir + uses: actions/cache/restore@v3 + id: verilator_restore + with: + path: /opt/verilator + key: verilator-${{ env.VERILATOR_VERSION }}-${{ env.SCCACHE_C_CUSTOM_CACHE_BUSTER }} + + - name: Restore Risc V Toolchain + uses: actions/cache/restore@v3 + id: riscv_restore + with: + path: /opt/riscv + key: riscv-${{ env.RISCV_VERSION }}-${{ env.SCCACHE_C_CUSTOM_CACHE_BUSTER }} + - name: Setup verilator path + run: | + echo /opt/verilator/bin >> $GITHUB_PATH + + - name: Setup riscv path run: | echo /opt/riscv/bin >> $GITHUB_PATH - name: Run Caliptra Verilator Smoke Test run: | - # TODO: Add the run_verilator_l0_regression.py script. - # Running a simple test to make sure that there aren't any elaboration issues CALIPTRA_ROOT=$(pwd) cd tools/scripts - make verilator CALIPTRA_ROOT=$CALIPTRA_ROOT TESTNAME=smoke_test_kv_uds_reset + make verilator CALIPTRA_ROOT=$CALIPTRA_ROOT TESTNAME=${{ matrix.test_name }} | tee output.log + # Search the last 30 lines of the output for "TESTCASE PASSED" + tail -n 30 output.log | grep "TESTCASE PASSED" + # grep will return 0 if the search term is found, and 1 otherwise + # A non-zero value will cause the github action to fail. + exit $? diff --git a/src/csrng/data/csrng.hjson b/src/csrng/data/csrng.hjson index 852bc4620..0c6c911f9 100644 --- a/src/csrng/data/csrng.hjson +++ b/src/csrng/data/csrng.hjson @@ -235,12 +235,43 @@ hwaccess: "hro", hwqe: "true", fields: [ - { bits: "31:0", - name: "CMD_REQ", + { bits: "3:0", + name: "acmd", + desc: ''' + Application Command: Selects one of five operations to perform. + The commands supported are instantiate, reseed, generate, update, + and uninstantiate. Each application interface port used by peripheral + hardware commands a unique instance number in CSRNG. + ''' + } + { bits: "7:4", + name: "clen", + desc: ''' + Command Length: Number of 32-bit words that can optionally be appended + to the command. A value of zero will only transfer the command header. + A value of 4'hc will transfer the header plus an additional twelve + 32-bit words of data. + ''' + } + { bits: "11:8", + name: "flag0", + desc: ''' + Command Flag0: flag0 is associated with current command. Setting this + field to kMultiBitBool4True will enable flag0 to be enabled. Note that + flag0 is used for the instantiate and reseed commands only, for all other commands its value is ignored. + ''' + } + { bits: "24:12", + name: "glen", desc: ''' - Writing this request with defined CSRNG commands will initiate all - possible CSRNG actions. The application interface must wait for the - "ack" to return before issuing new commands. + Generate Length: Only defined for the generate command, this field + is the total number of cryptographic entropy blocks requested. Each + unit represents 128 bits of entropy returned. The NIST reference name + is max_number_of_bit_per_request, and this field size supports the + maximum size of 219 bits. For the maximum size, this field should be + set to 4096, resulting in a max_number_of_bit_per_request value of + 4096 x 128 bits. For a smaller example, a value of 8 would return + a total of 1024 bits. ''' } ] diff --git a/src/csrng/data/csrng.json b/src/csrng/data/csrng.json index 3204f6446..b25e5c086 100644 --- a/src/csrng/data/csrng.json +++ b/src/csrng/data/csrng.json @@ -245,9 +245,24 @@ "hwqe": "true", "fields": [ { - "bits": "31:0", - "name": "CMD_REQ", - "desc": "Writing this request with defined CSRNG commands will initiate all\npossible CSRNG actions. The application interface must wait for the\n\"ack\" to return before issuing new commands." + "bits": "3:0", + "name": "acmd", + "desc": "Application Command: Selects one of five operations to perform.\nThe commands supported are instantiate, reseed, generate, update,\nand uninstantiate. Each application interface port used by peripheral\nhardware commands a unique instance number in CSRNG." + }, + { + "bits": "7:4", + "name": "clen", + "desc": "Command Length: Number of 32-bit words that can optionally be appended\nto the command. A value of zero will only transfer the command header.\nA value of 4'hc will transfer the header plus an additional twelve\n32-bit words of data." + }, + { + "bits": "11:8", + "name": "flag0", + "desc": "Command Flag0: flag0 is associated with current command. Setting this\nfield to kMultiBitBool4True will enable flag0 to be enabled. Note that\nflag0 is used for the instantiate and reseed commands only, for all other commands its value is ignored." + }, + { + "bits": "24:12", + "name": "glen", + "desc": "Generate Length: Only defined for the generate command, this field\nis the total number of cryptographic entropy blocks requested. Each\nunit represents 128 bits of entropy returned. The NIST reference name\nis max_number_of_bit_per_request, and this field size supports the\nmaximum size of 219 bits. For the maximum size, this field should be\nset to 4096, resulting in a max_number_of_bit_per_request value of\n4096 x 128 bits. For a smaller example, a value of 8 would return\na total of 1024 bits." } ] }, diff --git a/src/csrng/data/csrng.rdl b/src/csrng/data/csrng.rdl index 467238931..13d66a2eb 100644 --- a/src/csrng/data/csrng.rdl +++ b/src/csrng/data/csrng.rdl @@ -101,11 +101,36 @@ addrmap csrng { } CTRL @ 0x14; reg { field { - desc = "Writing this request with defined CSRNG commands will initiate all - possible CSRNG actions. The application interface must wait for the - \"ack\" to return before issuing new commands."; + desc = "Application Command: Selects one of five operations to perform. + The commands supported are instantiate, reseed, generate, update, + and uninstantiate. Each application interface port used by peripheral + hardware commands a unique instance number in CSRNG."; sw = w; - } CMD_REQ[31:0]; + } acmd[3:0]; + field { + desc = "Command Length: Number of 32-bit words that can optionally be appended + to the command. A value of zero will only transfer the command header. + A value of 4'hc will transfer the header plus an additional twelve + 32-bit words of data."; + sw = w; + } clen[7:4]; + field { + desc = "Command Flag0: flag0 is associated with current command. Setting this + field to kMultiBitBool4True will enable flag0 to be enabled. Note that + flag0 is used for the instantiate and reseed commands only, for all other commands its value is ignored."; + sw = w; + } flag0[11:8]; + field { + desc = "Generate Length: Only defined for the generate command, this field + is the total number of cryptographic entropy blocks requested. Each + unit represents 128 bits of entropy returned. The NIST reference name + is max_number_of_bit_per_request, and this field size supports the + maximum size of 219 bits. For the maximum size, this field should be + set to 4096, resulting in a max_number_of_bit_per_request value of + 4096 x 128 bits. For a smaller example, a value of 8 would return + a total of 1024 bits."; + sw = w; + } glen[24:12]; } CMD_REQ @ 0x18; reg { field { @@ -424,4 +449,4 @@ addrmap csrng { sw = r; } MAIN_SM_STATE[7:0] = 0x4E; } MAIN_SM_STATE @ 0x40; -}; \ No newline at end of file +}; diff --git a/src/integration/config/caliptra_top_tb.vf b/src/integration/config/caliptra_top_tb.vf index 7ee0cfb80..3760bc2f3 100644 --- a/src/integration/config/caliptra_top_tb.vf +++ b/src/integration/config/caliptra_top_tb.vf @@ -210,8 +210,10 @@ ${CALIPTRA_ROOT}/src/kmac/rtl/keccak_round.sv ${CALIPTRA_ROOT}/src/kmac/rtl/keccak_2share.sv ${CALIPTRA_ROOT}/src/kmac/rtl/sha3pad.sv ${CALIPTRA_ROOT}/src/kmac/rtl/sha3.sv +${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_flop_en.sv ${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_flop.sv ${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_buf.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_flop_en.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_flop_2sync.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_lfsr.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_mubi4_sync.sv @@ -286,4 +288,23 @@ ${CALIPTRA_ROOT}/src/csrng/rtl/csrng_block_encrypt.sv ${CALIPTRA_ROOT}/src/csrng/rtl/csrng_state_db.sv ${CALIPTRA_ROOT}/src/csrng/rtl/csrng_cmd_stage.sv ${CALIPTRA_ROOT}/src/csrng/rtl/csrng.sv -${CALIPTRA_ROOT}/src/integration/rtl/caliptra_top.sv \ No newline at end of file +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_reg_pkg.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_cmd_pkg.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_byte_merge.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_byte_select.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_command_queue.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_core.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_data_fifos.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_fsm.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_reg_top.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_shift_register.sv +${CALIPTRA_ROOT}/src/spi_host/tb/spi_device_pkg.sv +${CALIPTRA_ROOT}/src/spi_host/tb/spiflash.sv +${CALIPTRA_ROOT}/src/uart/rtl/uart_tx.sv +${CALIPTRA_ROOT}/src/uart/rtl/uart_reg_pkg.sv +${CALIPTRA_ROOT}/src/uart/rtl/uart_reg_top.sv +${CALIPTRA_ROOT}/src/uart/rtl/uart_rx.sv +${CALIPTRA_ROOT}/src/uart/rtl/uart.sv +${CALIPTRA_ROOT}/src/uart/rtl/uart_core.sv +${CALIPTRA_ROOT}/src/integration/rtl/caliptra_top.sv diff --git a/src/integration/config/compile.yml b/src/integration/config/compile.yml index e0abfa3b7..b1aa6e888 100644 --- a/src/integration/config/compile.yml +++ b/src/integration/config/compile.yml @@ -35,6 +35,8 @@ requires: - soc_ifc_top - entropy_src - csrng + - spi_host + - uart targets: rtl: directories: [$COMPILE_ROOT/rtl] diff --git a/src/integration/rtl/caliptra_reg.h b/src/integration/rtl/caliptra_reg.h index 10cd7924b..82028a21e 100644 --- a/src/integration/rtl/caliptra_reg.h +++ b/src/integration/rtl/caliptra_reg.h @@ -15,7 +15,6 @@ #ifndef CALIPTRA_REG_HEADER #define CALIPTRA_REG_HEADER - #define CLP_BASE_ADDR (0x0) #define CLP_DOE_REG_BASE_ADDR (0x10000000) #define CLP_DOE_REG_DOE_IV_0 (0x10000000) @@ -5591,4 +5590,4 @@ #define SOC_IFC_REG_INTR_BLOCK_RF_NOTIF_SOC_REQ_LOCK_INTR_COUNT_INCR_R_PULSE_MASK (0x1) -#endif \ No newline at end of file +#endif diff --git a/src/integration/rtl/caliptra_reg.rdl b/src/integration/rtl/caliptra_reg.rdl index c2b2108ab..6f98b9bca 100644 --- a/src/integration/rtl/caliptra_reg.rdl +++ b/src/integration/rtl/caliptra_reg.rdl @@ -32,6 +32,10 @@ addrmap clp { sha256_reg sha256_reg @ 0x1002_8000; + spi_host spi_host_reg @ 0x2000_0000; + + uart uart @ 0x2000_1000; + csrng csrng_reg @ 0x2000_2000; entropy_src entropy_src_reg @ 0x2000_3000; diff --git a/src/integration/rtl/caliptra_reg_defines.svh b/src/integration/rtl/caliptra_reg_defines.svh index 518848cbc..8b7c2851e 100644 --- a/src/integration/rtl/caliptra_reg_defines.svh +++ b/src/integration/rtl/caliptra_reg_defines.svh @@ -4336,6 +4336,292 @@ `define SHA256_REG_INTR_BLOCK_RF_NOTIF_CMD_DONE_INTR_COUNT_INCR_R (32'ha10) `define SHA256_REG_INTR_BLOCK_RF_NOTIF_CMD_DONE_INTR_COUNT_INCR_R_PULSE_LOW (0) `define SHA256_REG_INTR_BLOCK_RF_NOTIF_CMD_DONE_INTR_COUNT_INCR_R_PULSE_MASK (32'h1) +`define CLP_SPI_HOST_REG_BASE_ADDR (32'h20000000) +`define CLP_SPI_HOST_REG_INTERRUPT_STATE (32'h20000000) +`define SPI_HOST_REG_INTERRUPT_STATE (32'h0) +`define SPI_HOST_REG_INTERRUPT_STATE_ERROR_LOW (0) +`define SPI_HOST_REG_INTERRUPT_STATE_ERROR_MASK (32'h1) +`define SPI_HOST_REG_INTERRUPT_STATE_SPI_EVENT_LOW (1) +`define SPI_HOST_REG_INTERRUPT_STATE_SPI_EVENT_MASK (32'h2) +`define CLP_SPI_HOST_REG_INTERRUPT_ENABLE (32'h20000004) +`define SPI_HOST_REG_INTERRUPT_ENABLE (32'h4) +`define SPI_HOST_REG_INTERRUPT_ENABLE_ERROR_LOW (0) +`define SPI_HOST_REG_INTERRUPT_ENABLE_ERROR_MASK (32'h1) +`define SPI_HOST_REG_INTERRUPT_ENABLE_SPI_EVENT_LOW (1) +`define SPI_HOST_REG_INTERRUPT_ENABLE_SPI_EVENT_MASK (32'h2) +`define CLP_SPI_HOST_REG_INTERRUPT_TEST (32'h20000008) +`define SPI_HOST_REG_INTERRUPT_TEST (32'h8) +`define SPI_HOST_REG_INTERRUPT_TEST_ERROR_LOW (0) +`define SPI_HOST_REG_INTERRUPT_TEST_ERROR_MASK (32'h1) +`define SPI_HOST_REG_INTERRUPT_TEST_SPI_EVENT_LOW (1) +`define SPI_HOST_REG_INTERRUPT_TEST_SPI_EVENT_MASK (32'h2) +`define CLP_SPI_HOST_REG_ALERT_TEST (32'h2000000c) +`define SPI_HOST_REG_ALERT_TEST (32'hc) +`define SPI_HOST_REG_ALERT_TEST_FATAL_FAULT_LOW (0) +`define SPI_HOST_REG_ALERT_TEST_FATAL_FAULT_MASK (32'h1) +`define CLP_SPI_HOST_REG_CONTROL (32'h20000010) +`define SPI_HOST_REG_CONTROL (32'h10) +`define SPI_HOST_REG_CONTROL_RX_WATERMARK_LOW (0) +`define SPI_HOST_REG_CONTROL_RX_WATERMARK_MASK (32'hff) +`define SPI_HOST_REG_CONTROL_TX_WATERMARK_LOW (8) +`define SPI_HOST_REG_CONTROL_TX_WATERMARK_MASK (32'hff00) +`define SPI_HOST_REG_CONTROL_OUTPUT_EN_LOW (29) +`define SPI_HOST_REG_CONTROL_OUTPUT_EN_MASK (32'h20000000) +`define SPI_HOST_REG_CONTROL_SW_RST_LOW (30) +`define SPI_HOST_REG_CONTROL_SW_RST_MASK (32'h40000000) +`define SPI_HOST_REG_CONTROL_SPIEN_LOW (31) +`define SPI_HOST_REG_CONTROL_SPIEN_MASK (32'h80000000) +`define CLP_SPI_HOST_REG_STATUS (32'h20000014) +`define SPI_HOST_REG_STATUS (32'h14) +`define SPI_HOST_REG_STATUS_TXQD_LOW (0) +`define SPI_HOST_REG_STATUS_TXQD_MASK (32'hff) +`define SPI_HOST_REG_STATUS_RXQD_LOW (8) +`define SPI_HOST_REG_STATUS_RXQD_MASK (32'hff00) +`define SPI_HOST_REG_STATUS_CMDQD_LOW (16) +`define SPI_HOST_REG_STATUS_CMDQD_MASK (32'hf0000) +`define SPI_HOST_REG_STATUS_RXWM_LOW (20) +`define SPI_HOST_REG_STATUS_RXWM_MASK (32'h100000) +`define SPI_HOST_REG_STATUS_BYTEORDER_LOW (22) +`define SPI_HOST_REG_STATUS_BYTEORDER_MASK (32'h400000) +`define SPI_HOST_REG_STATUS_RXSTALL_LOW (23) +`define SPI_HOST_REG_STATUS_RXSTALL_MASK (32'h800000) +`define SPI_HOST_REG_STATUS_RXEMPTY_LOW (24) +`define SPI_HOST_REG_STATUS_RXEMPTY_MASK (32'h1000000) +`define SPI_HOST_REG_STATUS_RXFULL_LOW (25) +`define SPI_HOST_REG_STATUS_RXFULL_MASK (32'h2000000) +`define SPI_HOST_REG_STATUS_TXWM_LOW (26) +`define SPI_HOST_REG_STATUS_TXWM_MASK (32'h4000000) +`define SPI_HOST_REG_STATUS_TXSTALL_LOW (27) +`define SPI_HOST_REG_STATUS_TXSTALL_MASK (32'h8000000) +`define SPI_HOST_REG_STATUS_TXEMPTY_LOW (28) +`define SPI_HOST_REG_STATUS_TXEMPTY_MASK (32'h10000000) +`define SPI_HOST_REG_STATUS_TXFULL_LOW (29) +`define SPI_HOST_REG_STATUS_TXFULL_MASK (32'h20000000) +`define SPI_HOST_REG_STATUS_ACTIVE_LOW (30) +`define SPI_HOST_REG_STATUS_ACTIVE_MASK (32'h40000000) +`define SPI_HOST_REG_STATUS_READY_LOW (31) +`define SPI_HOST_REG_STATUS_READY_MASK (32'h80000000) +`define CLP_SPI_HOST_REG_CONFIGOPTS_0 (32'h20000018) +`define SPI_HOST_REG_CONFIGOPTS_0 (32'h18) +`define SPI_HOST_REG_CONFIGOPTS_0_CLKDIV_LOW (0) +`define SPI_HOST_REG_CONFIGOPTS_0_CLKDIV_MASK (32'hffff) +`define SPI_HOST_REG_CONFIGOPTS_0_CSNIDLE_LOW (16) +`define SPI_HOST_REG_CONFIGOPTS_0_CSNIDLE_MASK (32'hf0000) +`define SPI_HOST_REG_CONFIGOPTS_0_CSNTRAIL_LOW (20) +`define SPI_HOST_REG_CONFIGOPTS_0_CSNTRAIL_MASK (32'hf00000) +`define SPI_HOST_REG_CONFIGOPTS_0_CSNLEAD_LOW (24) +`define SPI_HOST_REG_CONFIGOPTS_0_CSNLEAD_MASK (32'hf000000) +`define SPI_HOST_REG_CONFIGOPTS_0_FULLCYC_LOW (29) +`define SPI_HOST_REG_CONFIGOPTS_0_FULLCYC_MASK (32'h20000000) +`define SPI_HOST_REG_CONFIGOPTS_0_CPHA_LOW (30) +`define SPI_HOST_REG_CONFIGOPTS_0_CPHA_MASK (32'h40000000) +`define SPI_HOST_REG_CONFIGOPTS_0_CPOL_LOW (31) +`define SPI_HOST_REG_CONFIGOPTS_0_CPOL_MASK (32'h80000000) +`define CLP_SPI_HOST_REG_CONFIGOPTS_1 (32'h2000001c) +`define SPI_HOST_REG_CONFIGOPTS_1 (32'h1c) +`define SPI_HOST_REG_CONFIGOPTS_1_CLKDIV_LOW (0) +`define SPI_HOST_REG_CONFIGOPTS_1_CLKDIV_MASK (32'hffff) +`define SPI_HOST_REG_CONFIGOPTS_1_CSNIDLE_LOW (16) +`define SPI_HOST_REG_CONFIGOPTS_1_CSNIDLE_MASK (32'hf0000) +`define SPI_HOST_REG_CONFIGOPTS_1_CSNTRAIL_LOW (20) +`define SPI_HOST_REG_CONFIGOPTS_1_CSNTRAIL_MASK (32'hf00000) +`define SPI_HOST_REG_CONFIGOPTS_1_CSNLEAD_LOW (24) +`define SPI_HOST_REG_CONFIGOPTS_1_CSNLEAD_MASK (32'hf000000) +`define SPI_HOST_REG_CONFIGOPTS_1_FULLCYC_LOW (29) +`define SPI_HOST_REG_CONFIGOPTS_1_FULLCYC_MASK (32'h20000000) +`define SPI_HOST_REG_CONFIGOPTS_1_CPHA_LOW (30) +`define SPI_HOST_REG_CONFIGOPTS_1_CPHA_MASK (32'h40000000) +`define SPI_HOST_REG_CONFIGOPTS_1_CPOL_LOW (31) +`define SPI_HOST_REG_CONFIGOPTS_1_CPOL_MASK (32'h80000000) +`define CLP_SPI_HOST_REG_CSID (32'h20000020) +`define SPI_HOST_REG_CSID (32'h20) +`define CLP_SPI_HOST_REG_COMMAND (32'h20000024) +`define SPI_HOST_REG_COMMAND (32'h24) +`define SPI_HOST_REG_COMMAND_LEN_LOW (0) +`define SPI_HOST_REG_COMMAND_LEN_MASK (32'h1ff) +`define SPI_HOST_REG_COMMAND_CSAAT_LOW (9) +`define SPI_HOST_REG_COMMAND_CSAAT_MASK (32'h200) +`define SPI_HOST_REG_COMMAND_SPEED_LOW (10) +`define SPI_HOST_REG_COMMAND_SPEED_MASK (32'hc00) +`define SPI_HOST_REG_COMMAND_DIRECTION_LOW (12) +`define SPI_HOST_REG_COMMAND_DIRECTION_MASK (32'h3000) +`define CLP_SPI_HOST_REG_RXDATA (32'h20000028) +`define SPI_HOST_REG_RXDATA (32'h28) +`define CLP_SPI_HOST_REG_TXDATA (32'h2000002c) +`define SPI_HOST_REG_TXDATA (32'h2c) +`define CLP_SPI_HOST_REG_ERROR_ENABLE (32'h20000030) +`define SPI_HOST_REG_ERROR_ENABLE (32'h30) +`define SPI_HOST_REG_ERROR_ENABLE_CMDBUSY_LOW (0) +`define SPI_HOST_REG_ERROR_ENABLE_CMDBUSY_MASK (32'h1) +`define SPI_HOST_REG_ERROR_ENABLE_OVERFLOW_LOW (1) +`define SPI_HOST_REG_ERROR_ENABLE_OVERFLOW_MASK (32'h2) +`define SPI_HOST_REG_ERROR_ENABLE_UNDERFLOW_LOW (2) +`define SPI_HOST_REG_ERROR_ENABLE_UNDERFLOW_MASK (32'h4) +`define SPI_HOST_REG_ERROR_ENABLE_CMDINVAL_LOW (3) +`define SPI_HOST_REG_ERROR_ENABLE_CMDINVAL_MASK (32'h8) +`define SPI_HOST_REG_ERROR_ENABLE_CSIDINVAL_LOW (4) +`define SPI_HOST_REG_ERROR_ENABLE_CSIDINVAL_MASK (32'h10) +`define CLP_SPI_HOST_REG_ERROR_STATUS (32'h20000034) +`define SPI_HOST_REG_ERROR_STATUS (32'h34) +`define SPI_HOST_REG_ERROR_STATUS_CMDBUSY_LOW (0) +`define SPI_HOST_REG_ERROR_STATUS_CMDBUSY_MASK (32'h1) +`define SPI_HOST_REG_ERROR_STATUS_OVERFLOW_LOW (1) +`define SPI_HOST_REG_ERROR_STATUS_OVERFLOW_MASK (32'h2) +`define SPI_HOST_REG_ERROR_STATUS_UNDERFLOW_LOW (2) +`define SPI_HOST_REG_ERROR_STATUS_UNDERFLOW_MASK (32'h4) +`define SPI_HOST_REG_ERROR_STATUS_CMDINVAL_LOW (3) +`define SPI_HOST_REG_ERROR_STATUS_CMDINVAL_MASK (32'h8) +`define SPI_HOST_REG_ERROR_STATUS_CSIDINVAL_LOW (4) +`define SPI_HOST_REG_ERROR_STATUS_CSIDINVAL_MASK (32'h10) +`define SPI_HOST_REG_ERROR_STATUS_ACCESSINVAL_LOW (5) +`define SPI_HOST_REG_ERROR_STATUS_ACCESSINVAL_MASK (32'h20) +`define CLP_SPI_HOST_REG_EVENT_ENABLE (32'h20000038) +`define SPI_HOST_REG_EVENT_ENABLE (32'h38) +`define SPI_HOST_REG_EVENT_ENABLE_RXFULL_LOW (0) +`define SPI_HOST_REG_EVENT_ENABLE_RXFULL_MASK (32'h1) +`define SPI_HOST_REG_EVENT_ENABLE_TXEMPTY_LOW (1) +`define SPI_HOST_REG_EVENT_ENABLE_TXEMPTY_MASK (32'h2) +`define SPI_HOST_REG_EVENT_ENABLE_RXWM_LOW (2) +`define SPI_HOST_REG_EVENT_ENABLE_RXWM_MASK (32'h4) +`define SPI_HOST_REG_EVENT_ENABLE_TXWM_LOW (3) +`define SPI_HOST_REG_EVENT_ENABLE_TXWM_MASK (32'h8) +`define SPI_HOST_REG_EVENT_ENABLE_READY_LOW (4) +`define SPI_HOST_REG_EVENT_ENABLE_READY_MASK (32'h10) +`define SPI_HOST_REG_EVENT_ENABLE_IDLE_LOW (5) +`define SPI_HOST_REG_EVENT_ENABLE_IDLE_MASK (32'h20) +`define CLP_UART_BASE_ADDR (32'h20001000) +`define CLP_UART_INTERRUPT_STATE (32'h20001000) +`define UART_INTERRUPT_STATE (32'h0) +`define UART_INTERRUPT_STATE_TX_WATERMARK_LOW (0) +`define UART_INTERRUPT_STATE_TX_WATERMARK_MASK (32'h1) +`define UART_INTERRUPT_STATE_RX_WATERMARK_LOW (1) +`define UART_INTERRUPT_STATE_RX_WATERMARK_MASK (32'h2) +`define UART_INTERRUPT_STATE_TX_EMPTY_LOW (2) +`define UART_INTERRUPT_STATE_TX_EMPTY_MASK (32'h4) +`define UART_INTERRUPT_STATE_RX_OVERFLOW_LOW (3) +`define UART_INTERRUPT_STATE_RX_OVERFLOW_MASK (32'h8) +`define UART_INTERRUPT_STATE_RX_FRAME_ERR_LOW (4) +`define UART_INTERRUPT_STATE_RX_FRAME_ERR_MASK (32'h10) +`define UART_INTERRUPT_STATE_RX_BREAK_ERR_LOW (5) +`define UART_INTERRUPT_STATE_RX_BREAK_ERR_MASK (32'h20) +`define UART_INTERRUPT_STATE_RX_TIMEOUT_LOW (6) +`define UART_INTERRUPT_STATE_RX_TIMEOUT_MASK (32'h40) +`define UART_INTERRUPT_STATE_RX_PARITY_ERR_LOW (7) +`define UART_INTERRUPT_STATE_RX_PARITY_ERR_MASK (32'h80) +`define CLP_UART_INTERRUPT_ENABLE (32'h20001004) +`define UART_INTERRUPT_ENABLE (32'h4) +`define UART_INTERRUPT_ENABLE_TX_WATERMARK_LOW (0) +`define UART_INTERRUPT_ENABLE_TX_WATERMARK_MASK (32'h1) +`define UART_INTERRUPT_ENABLE_RX_WATERMARK_LOW (1) +`define UART_INTERRUPT_ENABLE_RX_WATERMARK_MASK (32'h2) +`define UART_INTERRUPT_ENABLE_TX_EMPTY_LOW (2) +`define UART_INTERRUPT_ENABLE_TX_EMPTY_MASK (32'h4) +`define UART_INTERRUPT_ENABLE_RX_OVERFLOW_LOW (3) +`define UART_INTERRUPT_ENABLE_RX_OVERFLOW_MASK (32'h8) +`define UART_INTERRUPT_ENABLE_RX_FRAME_ERR_LOW (4) +`define UART_INTERRUPT_ENABLE_RX_FRAME_ERR_MASK (32'h10) +`define UART_INTERRUPT_ENABLE_RX_BREAK_ERR_LOW (5) +`define UART_INTERRUPT_ENABLE_RX_BREAK_ERR_MASK (32'h20) +`define UART_INTERRUPT_ENABLE_RX_TIMEOUT_LOW (6) +`define UART_INTERRUPT_ENABLE_RX_TIMEOUT_MASK (32'h40) +`define UART_INTERRUPT_ENABLE_RX_PARITY_ERR_LOW (7) +`define UART_INTERRUPT_ENABLE_RX_PARITY_ERR_MASK (32'h80) +`define CLP_UART_INTERRUPT_TEST (32'h20001008) +`define UART_INTERRUPT_TEST (32'h8) +`define UART_INTERRUPT_TEST_TX_WATERMARK_LOW (0) +`define UART_INTERRUPT_TEST_TX_WATERMARK_MASK (32'h1) +`define UART_INTERRUPT_TEST_RX_WATERMARK_LOW (1) +`define UART_INTERRUPT_TEST_RX_WATERMARK_MASK (32'h2) +`define UART_INTERRUPT_TEST_TX_EMPTY_LOW (2) +`define UART_INTERRUPT_TEST_TX_EMPTY_MASK (32'h4) +`define UART_INTERRUPT_TEST_RX_OVERFLOW_LOW (3) +`define UART_INTERRUPT_TEST_RX_OVERFLOW_MASK (32'h8) +`define UART_INTERRUPT_TEST_RX_FRAME_ERR_LOW (4) +`define UART_INTERRUPT_TEST_RX_FRAME_ERR_MASK (32'h10) +`define UART_INTERRUPT_TEST_RX_BREAK_ERR_LOW (5) +`define UART_INTERRUPT_TEST_RX_BREAK_ERR_MASK (32'h20) +`define UART_INTERRUPT_TEST_RX_TIMEOUT_LOW (6) +`define UART_INTERRUPT_TEST_RX_TIMEOUT_MASK (32'h40) +`define UART_INTERRUPT_TEST_RX_PARITY_ERR_LOW (7) +`define UART_INTERRUPT_TEST_RX_PARITY_ERR_MASK (32'h80) +`define CLP_UART_ALERT_TEST (32'h2000100c) +`define UART_ALERT_TEST (32'hc) +`define UART_ALERT_TEST_FATAL_FAULT_LOW (0) +`define UART_ALERT_TEST_FATAL_FAULT_MASK (32'h1) +`define CLP_UART_CTRL (32'h20001010) +`define UART_CTRL (32'h10) +`define UART_CTRL_TX_LOW (0) +`define UART_CTRL_TX_MASK (32'h1) +`define UART_CTRL_RX_LOW (1) +`define UART_CTRL_RX_MASK (32'h2) +`define UART_CTRL_NF_LOW (2) +`define UART_CTRL_NF_MASK (32'h4) +`define UART_CTRL_SLPBK_LOW (4) +`define UART_CTRL_SLPBK_MASK (32'h10) +`define UART_CTRL_LLPBK_LOW (5) +`define UART_CTRL_LLPBK_MASK (32'h20) +`define UART_CTRL_PARITY_EN_LOW (6) +`define UART_CTRL_PARITY_EN_MASK (32'h40) +`define UART_CTRL_PARITY_ODD_LOW (7) +`define UART_CTRL_PARITY_ODD_MASK (32'h80) +`define UART_CTRL_RXBLVL_LOW (8) +`define UART_CTRL_RXBLVL_MASK (32'h300) +`define UART_CTRL_NCO_LOW (16) +`define UART_CTRL_NCO_MASK (32'hffff0000) +`define CLP_UART_STATUS (32'h20001014) +`define UART_STATUS (32'h14) +`define UART_STATUS_TXFULL_LOW (0) +`define UART_STATUS_TXFULL_MASK (32'h1) +`define UART_STATUS_RXFULL_LOW (1) +`define UART_STATUS_RXFULL_MASK (32'h2) +`define UART_STATUS_TXEMPTY_LOW (2) +`define UART_STATUS_TXEMPTY_MASK (32'h4) +`define UART_STATUS_TXIDLE_LOW (3) +`define UART_STATUS_TXIDLE_MASK (32'h8) +`define UART_STATUS_RXIDLE_LOW (4) +`define UART_STATUS_RXIDLE_MASK (32'h10) +`define UART_STATUS_RXEMPTY_LOW (5) +`define UART_STATUS_RXEMPTY_MASK (32'h20) +`define CLP_UART_RDATA (32'h20001018) +`define UART_RDATA (32'h18) +`define UART_RDATA_RDATA_LOW (0) +`define UART_RDATA_RDATA_MASK (32'hff) +`define CLP_UART_WDATA (32'h2000101c) +`define UART_WDATA (32'h1c) +`define UART_WDATA_WDATA_LOW (0) +`define UART_WDATA_WDATA_MASK (32'hff) +`define CLP_UART_FIFO_CTRL (32'h20001020) +`define UART_FIFO_CTRL (32'h20) +`define UART_FIFO_CTRL_RXRST_LOW (0) +`define UART_FIFO_CTRL_RXRST_MASK (32'h1) +`define UART_FIFO_CTRL_TXRST_LOW (1) +`define UART_FIFO_CTRL_TXRST_MASK (32'h2) +`define UART_FIFO_CTRL_RXILVL_LOW (2) +`define UART_FIFO_CTRL_RXILVL_MASK (32'h1c) +`define UART_FIFO_CTRL_TXILVL_LOW (5) +`define UART_FIFO_CTRL_TXILVL_MASK (32'h60) +`define CLP_UART_FIFO_STATUS (32'h20001024) +`define UART_FIFO_STATUS (32'h24) +`define UART_FIFO_STATUS_TXLVL_LOW (0) +`define UART_FIFO_STATUS_TXLVL_MASK (32'h3f) +`define UART_FIFO_STATUS_RXLVL_LOW (16) +`define UART_FIFO_STATUS_RXLVL_MASK (32'h3f0000) +`define CLP_UART_OVRD (32'h20001028) +`define UART_OVRD (32'h28) +`define UART_OVRD_TXEN_LOW (0) +`define UART_OVRD_TXEN_MASK (32'h1) +`define UART_OVRD_TXVAL_LOW (1) +`define UART_OVRD_TXVAL_MASK (32'h2) +`define CLP_UART_VAL (32'h2000102c) +`define UART_VAL (32'h2c) +`define UART_VAL_RX_LOW (0) +`define UART_VAL_RX_MASK (32'hffff) +`define CLP_UART_TIMEOUT_CTRL (32'h20001030) +`define UART_TIMEOUT_CTRL (32'h30) +`define UART_TIMEOUT_CTRL_VAL_LOW (0) +`define UART_TIMEOUT_CTRL_VAL_MASK (32'hffffff) +`define UART_TIMEOUT_CTRL_EN_LOW (31) +`define UART_TIMEOUT_CTRL_EN_MASK (32'h80000000) `define CLP_CSRNG_REG_BASE_ADDR (32'h20002000) `define CLP_CSRNG_REG_INTERRUPT_STATE (32'h20002000) `define CSRNG_REG_INTERRUPT_STATE (32'h0) @@ -4387,6 +4673,14 @@ `define CSRNG_REG_CTRL_READ_INT_STATE_MASK (32'hf00) `define CLP_CSRNG_REG_CMD_REQ (32'h20002018) `define CSRNG_REG_CMD_REQ (32'h18) +`define CSRNG_REG_CMD_REQ_ACMD_LOW (0) +`define CSRNG_REG_CMD_REQ_ACMD_MASK (32'hf) +`define CSRNG_REG_CMD_REQ_CLEN_LOW (4) +`define CSRNG_REG_CMD_REQ_CLEN_MASK (32'hf0) +`define CSRNG_REG_CMD_REQ_FLAG0_LOW (8) +`define CSRNG_REG_CMD_REQ_FLAG0_MASK (32'hf00) +`define CSRNG_REG_CMD_REQ_GLEN_LOW (12) +`define CSRNG_REG_CMD_REQ_GLEN_MASK (32'h1fff000) `define CLP_CSRNG_REG_SW_CMD_STS (32'h2000201c) `define CSRNG_REG_SW_CMD_STS (32'h1c) `define CSRNG_REG_SW_CMD_STS_CMD_RDY_LOW (0) @@ -5192,6 +5486,8 @@ `define SOC_IFC_REG_CPTRA_HW_CONFIG_QSPI_EN_MASK (32'h2) `define SOC_IFC_REG_CPTRA_HW_CONFIG_I3C_EN_LOW (2) `define SOC_IFC_REG_CPTRA_HW_CONFIG_I3C_EN_MASK (32'h4) +`define SOC_IFC_REG_CPTRA_HW_CONFIG_UART_EN_LOW (3) +`define SOC_IFC_REG_CPTRA_HW_CONFIG_UART_EN_MASK (32'h8) `define CLP_SOC_IFC_REG_CPTRA_WDT_TIMER1_EN (32'h300300e0) `define SOC_IFC_REG_CPTRA_WDT_TIMER1_EN (32'he0) `define SOC_IFC_REG_CPTRA_WDT_TIMER1_EN_TIMER1_EN_LOW (0) diff --git a/src/integration/rtl/caliptra_top.sv b/src/integration/rtl/caliptra_top.sv index 471c71595..1dcd23645 100755 --- a/src/integration/rtl/caliptra_top.sv +++ b/src/integration/rtl/caliptra_top.sv @@ -54,12 +54,16 @@ module caliptra_top output logic [`CALIPTRA_APB_DATA_WIDTH-1:0] PRDATA, //QSPI Interface - output logic qspi_clk_o, + output logic qspi_clk_o, output logic [`CALIPTRA_QSPI_CS_WIDTH-1:0] qspi_cs_no, inout wire [`CALIPTRA_QSPI_IO_WIDTH-1:0] qspi_d_io, //UART Interface - //TODO update with UART interface signals + // TODO: Determine if this should be set behind a ifdef +`ifdef CALIPTRA_INTERNAL_UART + output logic uart_tx, + input logic uart_rx, +`endif //I3C Interface //TODO update with I3C interface signals @@ -346,10 +350,6 @@ assign uart_error_intr = 1'b0; // TODO assign uart_notif_intr = 1'b0; // TODO assign i3c_error_intr = 1'b0; // TODO assign i3c_notif_intr = 1'b0; // TODO -//QSPI Tie Off -assign qspi_clk_o = '0; -assign qspi_cs_no = '0; -assign qspi_d_io = '0; // Vector 0 usage is reserved by VeeR, so bit 0 of the intr wire // drive Vector 1 @@ -964,6 +964,102 @@ entropy_src #( `endif + +`ifdef CALIPTRA_INTERNAL_QSPI + + logic cio_sck_o; + logic cio_sck_en_o; + logic [`CALIPTRA_QSPI_CS_WIDTH-1:0] cio_csb_o; + logic [`CALIPTRA_QSPI_CS_WIDTH-1:0] cio_csb_en_o; + logic [`CALIPTRA_QSPI_IO_WIDTH-1:0] cio_sd_o; + logic [`CALIPTRA_QSPI_IO_WIDTH-1:0] cio_sd_en_o; + logic [`CALIPTRA_QSPI_IO_WIDTH-1:0] cio_sd_i; + + assign qspi_clk_o = cio_sck_en_o ? cio_sck_o : 1'b0; + for (genvar ii = 0; ii < `CALIPTRA_QSPI_CS_WIDTH; ii += 1) begin : gen_qspi_cs + assign qspi_cs_no[ii] = cio_csb_en_o[ii] ? cio_csb_o[ii] : 1'b1; + end + for (genvar ii = 0; ii < `CALIPTRA_QSPI_IO_WIDTH; ii += 1) begin : gen_qspi_io + assign qspi_d_io[ii] = cio_sd_en_o[ii] ? cio_sd_o[ii] : 1'bz; + assign cio_sd_i[ii] = cio_sd_en_o[ii] ? 1'bz : qspi_d_io[ii]; + end + +spi_host #( + .AHBDataWidth(`CALIPTRA_AHB_HDATA_SIZE), + .AHBAddrWidth(`CALIPTRA_SLAVE_ADDR_WIDTH(`CALIPTRA_SLAVE_SEL_CSRNG)) +) spi_host ( + // Clock and reset connections + .clk_i (clk_cg), + .rst_ni (cptra_noncore_rst_b), + // AMBA AHB Lite Interface + .haddr_i (responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].haddr[`CALIPTRA_SLAVE_ADDR_WIDTH(`CALIPTRA_SLAVE_SEL_QSPI)-1:0]), + .hwdata_i (responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hwdata), + .hsel_i (responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hsel), + .hwrite_i (responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hwrite), + .hready_i (responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hready), + .htrans_i (responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].htrans), + .hsize_i (responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hsize), + .hresp_o (responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hresp), + .hreadyout_o (responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hreadyout), + .hrdata_o (responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hrdata), + // Alerts + .alert_rx_i(prim_alert_pkg::ALERT_RX_DEFAULT), + .alert_tx_o(), + // SPI Interface + .cio_sck_o (cio_sck_o), + .cio_sck_en_o (cio_sck_en_o), + .cio_csb_o (cio_csb_o), + .cio_csb_en_o (cio_csb_en_o), + .cio_sd_o (cio_sd_o), + .cio_sd_en_o (cio_sd_en_o), + .cio_sd_i (cio_sd_i), + .intr_error_o(), + .intr_spi_event_o() + ); +`else +//QSPI Tie Off +assign qspi_clk_o = '0; +assign qspi_cs_no = '0; +assign qspi_d_io = '0; +`endif + +`ifdef CALIPTRA_INTERNAL_UART +uart #( + .AHBDataWidth(`CALIPTRA_AHB_HDATA_SIZE), + .AHBAddrWidth(`CALIPTRA_SLAVE_ADDR_WIDTH(`CALIPTRA_SLAVE_SEL_UART)) +) uart ( + .clk_i (clk_cg), + .rst_ni (cptra_noncore_rst_b), + // AMBA AHB Lite Interface + .haddr_i (responder_inst[`CALIPTRA_SLAVE_SEL_UART].haddr[`CALIPTRA_SLAVE_ADDR_WIDTH(`CALIPTRA_SLAVE_SEL_UART)-1:0]), + .hwdata_i (responder_inst[`CALIPTRA_SLAVE_SEL_UART].hwdata), + .hsel_i (responder_inst[`CALIPTRA_SLAVE_SEL_UART].hsel), + .hwrite_i (responder_inst[`CALIPTRA_SLAVE_SEL_UART].hwrite), + .hready_i (responder_inst[`CALIPTRA_SLAVE_SEL_UART].hready), + .htrans_i (responder_inst[`CALIPTRA_SLAVE_SEL_UART].htrans), + .hsize_i (responder_inst[`CALIPTRA_SLAVE_SEL_UART].hsize), + .hresp_o (responder_inst[`CALIPTRA_SLAVE_SEL_UART].hresp), + .hreadyout_o (responder_inst[`CALIPTRA_SLAVE_SEL_UART].hreadyout), + .hrdata_o (responder_inst[`CALIPTRA_SLAVE_SEL_UART].hrdata), + // Alerts + .alert_rx_i (), + .alert_tx_o (), + // Generic IO + .cio_rx_i(uart_rx), + .cio_tx_o(uart_tx), + .cio_tx_en_o(), + // Interrupts + .intr_tx_watermark_o(), + .intr_rx_watermark_o(), + .intr_tx_empty_o(), + .intr_rx_overflow_o(), + .intr_rx_frame_err_o(), + .intr_rx_break_err_o(), + .intr_rx_timeout_o(), + .intr_rx_parity_err_o() + ); +`endif + soc_ifc_top #( .AHB_ADDR_WIDTH(`CALIPTRA_SLAVE_ADDR_WIDTH(`CALIPTRA_SLAVE_SEL_SOC_IFC)), .AHB_DATA_WIDTH(`CALIPTRA_AHB_HDATA_SIZE), @@ -1058,12 +1154,16 @@ soc_ifc_top1 //TIE OFF slaves always_comb begin: tie_off_slaves +`ifndef CALIPTRA_INTERNAL_QSPI responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hresp = '0; responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hreadyout = '0; responder_inst[`CALIPTRA_SLAVE_SEL_QSPI].hrdata = '0; +`endif +`ifndef CALIPTRA_INTERNAL_UART responder_inst[`CALIPTRA_SLAVE_SEL_UART].hresp = '0; responder_inst[`CALIPTRA_SLAVE_SEL_UART].hreadyout = '0; responder_inst[`CALIPTRA_SLAVE_SEL_UART].hrdata = '0; +`endif responder_inst[`CALIPTRA_SLAVE_SEL_I3C].hresp = '0; responder_inst[`CALIPTRA_SLAVE_SEL_I3C].hreadyout = '0; responder_inst[`CALIPTRA_SLAVE_SEL_I3C].hrdata = '0; diff --git a/src/integration/rtl/config_defines.svh b/src/integration/rtl/config_defines.svh index a4bb6c44a..44d5230b8 100755 --- a/src/integration/rtl/config_defines.svh +++ b/src/integration/rtl/config_defines.svh @@ -31,8 +31,8 @@ // AHB Address Map `define CALIPTRA_SLAVE_NAMES {"ENTROPY_SRC", "CSRNG" , "IMEM" , "SHA256" , "VEER_ICCM_DMA" , "VEER_DCCM_DMA" , "SOC_IFC" , "I3C" , "UART" , "QSPI" , "SHA512" , "DATAVAULT" , "PCRVAULT" , "KEYVAULT" , "HMAC" , "ECC" , "DOE_CTRL" } /* Array of names for peripherals */ - `define CALIPTRA_SLAVE_BASE_ADDR {32'h2000_3000, 32'h2000_2000, 32'h0000_0000, 32'h1002_8000, 32'h4000_0000 , 32'h5000_0000 , 32'h3000_0000, 32'hFFFF_FFFF, 32'hFFFF_FFFF, 32'hFFFF_FFFF, 32'h1002_0000, 32'h1001_C000, 32'h1001_A000, 32'h1001_8000, 32'h1001_0000, 32'h1000_8000, 32'h1000_0000} /* Array with slave base address */ - `define CALIPTRA_SLAVE_MASK_ADDR {32'h2000_3FFF, 32'h2000_2FFF, 32'h0000_8000, 32'h1002_FFFF, 32'h4001_FFFF , 32'h5001_FFFF , 32'h3003_FFFF, 32'hFFFF_FFFF, 32'hFFFF_FFFF, 32'hFFFF_FFFF, 32'h1002_7FFF, 32'h1001_DFFF, 32'h1001_BFFF, 32'h1001_9FFF, 32'h1001_0FFF, 32'h1000_FFFF, 32'h1000_7FFF} /* Array with slave offset address */ + `define CALIPTRA_SLAVE_BASE_ADDR {32'h2000_3000, 32'h2000_2000, 32'h0000_0000, 32'h1002_8000, 32'h4000_0000 , 32'h5000_0000 , 32'h3000_0000, 32'hFFFF_FFFF, 32'h2000_1000, 32'h2000_0000, 32'h1002_0000, 32'h1001_C000, 32'h1001_A000, 32'h1001_8000, 32'h1001_0000, 32'h1000_8000, 32'h1000_0000} /* Array with slave base address */ + `define CALIPTRA_SLAVE_MASK_ADDR {32'h2000_3FFF, 32'h2000_2FFF, 32'h0000_8000, 32'h1002_FFFF, 32'h4001_FFFF , 32'h5001_FFFF , 32'h3003_FFFF, 32'hFFFF_FFFF, 32'h2000_1FFF, 32'h2000_0FFF, 32'h1002_7FFF, 32'h1001_DFFF, 32'h1001_BFFF, 32'h1001_9FFF, 32'h1001_0FFF, 32'h1000_FFFF, 32'h1000_7FFF} /* Array with slave offset address */ `define CALIPTRA_SLAVE_ADDR_MASK (`CALIPTRA_SLAVE_BASE_ADDR ^ `CALIPTRA_SLAVE_MASK_ADDR) /* Array indicating meaningful address bits for each slave */ `define CALIPTRA_SLAVE_ADDR_WIDTH(n) $clog2((`CALIPTRA_SLAVE_ADDR_MASK >> (`CALIPTRA_AHB_HADDR_SIZE*n)) & {`CALIPTRA_AHB_HADDR_SIZE{1'b1}}) /* Decode address width for each slave from assigned BASE/MASK address */ `define CALIPTRA_SLAVE_SEL_DOE 0 diff --git a/src/integration/tb/caliptra_top_tb.sv b/src/integration/tb/caliptra_top_tb.sv index 241284dee..ad0f48c12 100755 --- a/src/integration/tb/caliptra_top_tb.sv +++ b/src/integration/tb/caliptra_top_tb.sv @@ -102,6 +102,15 @@ module caliptra_top_tb ( logic PSLVERR; logic [`CALIPTRA_APB_DATA_WIDTH-1:0] PRDATA; + // QSPI Interface + logic qspi_clk; + logic [`CALIPTRA_QSPI_CS_WIDTH-1:0] qspi_cs_n; + wire [`CALIPTRA_QSPI_IO_WIDTH-1:0] qspi_data; + +`ifdef CALIPTRA_INTERNAL_UART + logic uart_loopback; +`endif + logic ready_for_fuses; logic ready_for_fw_push; logic mailbox_data_avail; @@ -629,9 +638,14 @@ caliptra_top caliptra_top_dut ( .PWDATA(PWDATA), .PWRITE(PWRITE), - .qspi_clk_o(), - .qspi_cs_no(), - .qspi_d_io(), + .qspi_clk_o(qspi_clk), + .qspi_cs_no(qspi_cs_n), + .qspi_d_io(qspi_data), + +`ifdef CALIPTRA_INTERNAL_UART + .uart_tx(uart_loopback), + .uart_rx(uart_loopback), +`endif .el2_mem_export(el2_mem_export), @@ -685,6 +699,33 @@ physical_rng physical_rng ( .data (itrng_data), .valid (itrng_valid) ); +`endif + +`ifdef CALIPTRA_INTERNAL_QSPI + //=========================================================================- + // SPI Flash + //=========================================================================- +localparam logic [15:0] DeviceId0 = 16'hF10A; +localparam logic [15:0] DeviceId1 = 16'hF10B; + +spiflash #( + .DeviceId(DeviceId0), + .SpiFlashRandomData(0) // fixed pattern for smoke test +) spiflash0 ( + .sck (qspi_clk), + .csb (qspi_cs_n[0]), + .sd (qspi_data) +); + +spiflash #( + .DeviceId(DeviceId1), + .SpiFlashRandomData(0) // fixed pattern for smoke test +) spiflash1 ( + .sck (qspi_clk), + .csb (qspi_cs_n[1]), + .sd (qspi_data) +); + `endif //=========================================================================- diff --git a/src/integration/tb/caliptra_top_tb_services.sv b/src/integration/tb/caliptra_top_tb_services.sv index a498c6d48..dcc39632b 100644 --- a/src/integration/tb/caliptra_top_tb_services.sv +++ b/src/integration/tb/caliptra_top_tb_services.sv @@ -661,7 +661,7 @@ module caliptra_top_tb_services cycleCnt <= cycleCnt+1; // Test timeout monitor if(cycleCnt == MAX_CYCLES && !UVM_TB) begin - $display ("Hit max cycle count (%0d) .. stopping",cycleCnt); + $error("Hit max cycle count (%0d) .. stopping",cycleCnt); dump_memory_contents(MEMTYPE_LMEM, 32'h8000_0110, 32'h8000_0180); dump_memory_contents(MEMTYPE_DCCM, `RV_DCCM_SADR, `RV_DCCM_EADR); dump_memory_contents(MEMTYPE_ICCM, `RV_ICCM_SADR, `RV_ICCM_EADR); @@ -701,12 +701,12 @@ module caliptra_top_tb_services if (UVM_TB) $info("INFO: Detected FW write to manually end the test with FAIL; ignoring since the UVM environment will handle this."); else begin cycleCntKillReq <= cycleCnt; - $display("* TESTCASE FAILED"); + $error("* TESTCASE FAILED"); $display(" -- Extending simulation for 100 clock cycles to capture ending waveform"); end end if (|cycleCntKillReq && (cycleCnt == (cycleCntKillReq + 100))) begin - $display("Dumping memory contents at simulation end due to FAILURE"); + $error("Dumping memory contents at simulation end due to FAILURE"); dump_memory_contents(MEMTYPE_LMEM, 32'h0000_0000, 32'h001_FFFF); dump_memory_contents(MEMTYPE_DCCM, `RV_DCCM_SADR, `RV_DCCM_EADR); dump_memory_contents(MEMTYPE_ICCM, `RV_ICCM_SADR, `RV_ICCM_EADR); diff --git a/src/integration/test_suites/smoke_test_qspi/caliptra_isr.h b/src/integration/test_suites/smoke_test_qspi/caliptra_isr.h new file mode 100644 index 000000000..23b511c4b --- /dev/null +++ b/src/integration/test_suites/smoke_test_qspi/caliptra_isr.h @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// --------------------------------------------------------------------- +// File: caliptra_isr.h +// Description: +// Provides function declarations for use by external test files, so +// that the ISR functionality may behave like a library. +// TODO: +// This header file includes inline function definitions for event and +// test specific interrupt service behavior, so it should be copied and +// modified for each test. +// --------------------------------------------------------------------- + +#ifndef CALIPTRA_ISR_H + #define CALIPTRA_ISR_H + +#define EN_ISR_PRINTS 1 + +#include "caliptra_defines.h" +#include +#include "printf.h" + +//extern volatile uint32_t hmac_intr_status; + +////////////////////////////////////////////////////////////////////////////// +// Function Declarations +// +// Performs all the CSR setup to configure and enable vectored external interrupts +void init_interrupts(void); + +// These inline functions are used to insert event-specific functionality into the +// otherwise generic ISR that gets laid down by the parameterized macro "nonstd_swerv_isr" +inline void service_doe_error_intr() {printf("ERROR");} +inline void service_doe_notif_intr() {printf("ERROR");} +inline void service_ecc_error_intr () {printf("ERROR");} +inline void service_ecc_notif_intr () {printf("ERROR");} +inline void service_hmac_error_intr () {printf("ERROR");} +inline void service_hmac_notif_intr () {printf("ERROR");} + +inline void service_kv_error_intr () {printf("ERROR");} +inline void service_kv_notif_intr () {printf("ERROR");} +inline void service_sha512_error_intr() {printf("ERROR");} +inline void service_sha512_notif_intr() {printf("ERROR");} +inline void service_sha256_error_intr() {printf("ERROR");} +inline void service_sha256_notif_intr() {printf("ERROR");} +inline void service_qspi_error_intr () {printf("ERROR");} +inline void service_qspi_notif_intr () {printf("ERROR");} +inline void service_uart_error_intr () {printf("ERROR");} +inline void service_uart_notif_intr () {printf("ERROR");} +inline void service_i3c_error_intr () {printf("ERROR");} +inline void service_i3c_notif_intr () {printf("ERROR");} +inline void service_soc_ifc_error_intr () {printf("ERROR");} +inline void service_soc_ifc_notif_intr () {printf("ERROR");} +inline void service_sha512_acc_error_intr() {printf("ERROR");} +inline void service_sha512_acc_notif_intr() {printf("ERROR");} + + +#endif //CALIPTRA_ISR_H diff --git a/src/integration/test_suites/smoke_test_qspi/smoke_test_qspi.c b/src/integration/test_suites/smoke_test_qspi/smoke_test_qspi.c new file mode 100644 index 000000000..2ddae7c85 --- /dev/null +++ b/src/integration/test_suites/smoke_test_qspi/smoke_test_qspi.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include +#include + +#include "caliptra_defines.h" +#include "caliptra_isr.h" +#include "printf.h" +#include "riscv-csr.h" +#include "riscv_hw_if.h" + +volatile char *stdout = (char *)STDOUT; +volatile uint32_t intr_count = 0; +volatile uint32_t rst_count __attribute__((section(".dccm.persistent"))) = 0; +#ifdef CPT_VERBOSITY +enum printf_verbosity verbosity_g = CPT_VERBOSITY; +#else +enum printf_verbosity verbosity_g = LOW; +#endif + +typedef enum { Dummy = 0, RdOnly = 1, WrOnly = 2, BiDir = 3 } direction_t; +typedef enum { Standard = 0, Dual = 1, Quad = 2 } speed_t; +typedef enum { CmdJedecId = 0x9f, CmdReadQuad = 0x6b } cmd_spi_t; + +// Parameters +int NUM_QSPI = 2; + +// read data and compare against expected value. If there is no error, return 0 +int read_and_compare(uint32_t addr, uint32_t exp_data) { + uint32_t act_data; + act_data = lsu_read_32(addr); + if (act_data != exp_data) { + printf("Got:%x Want: %x\n", act_data, exp_data); + return 1; + } + return 0; +} + +void end_sim_if_qspi_disabled() { + uint32_t hw_cfg; + hw_cfg = lsu_read_32(CLP_SOC_IFC_REG_CPTRA_HW_CONFIG); + if (hw_cfg & SOC_IFC_REG_CPTRA_HW_CONFIG_QSPI_EN_MASK) { + VPRINTF(LOW, "Internal QSPI is enabled, running QSPI smoke test\n"); + } else { + VPRINTF(FATAL, "Internal QSPI is not enabled, skipping QSPI smoke test\n"); + SEND_STDOUT_CTRL(0xFF); + while (1) + ; + } +} + +void set_spi_csid(int host) { lsu_write_32(CLP_SPI_HOST_REG_CSID, host); } + +void spi_command(int length, int csaat, speed_t speed, direction_t direction) { + uint32_t status; + + printf(" - Writing spi_command: len:%d csaat:%d speed:%d direction:%d\n", + length, csaat, speed, direction); + + // Wait for Status.ready + do { + status = lsu_read_32(CLP_SPI_HOST_REG_STATUS); + } while (0 == (status & SPI_HOST_REG_STATUS_READY_MASK)); + + lsu_write_32(CLP_SPI_HOST_REG_COMMAND, + (direction << SPI_HOST_REG_COMMAND_DIRECTION_LOW) | + (speed << SPI_HOST_REG_COMMAND_SPEED_LOW) | + (csaat << SPI_HOST_REG_COMMAND_CSAAT_LOW) | + (length << SPI_HOST_REG_COMMAND_LEN_LOW)); +} + +void spi_command_wait() { + uint32_t status; + + // Wait for Status.active + do { + status = lsu_read_32(CLP_SPI_HOST_REG_STATUS); + } while (0 == (status & SPI_HOST_REG_STATUS_ACTIVE_MASK)); +} + +int fifo_rx_wait(int queue_depth) { + uint32_t rxqd, status; + int timeout = 0; + do { + status = lsu_read_32(CLP_SPI_HOST_REG_STATUS); + rxqd = ((status & SPI_HOST_REG_STATUS_RXQD_MASK) >> + SPI_HOST_REG_STATUS_RXQD_LOW); + timeout++; + if (timeout > 1000) { + printf( + "fifo_rx_wait: timed out waiting for queue_depth = %d. status:0x%x " + "rxqd: %d\n", + queue_depth, status, rxqd); + return 1; + } + } while (rxqd != queue_depth); + return 0; +} + +void write_tx_fifo(uint32_t data) { + lsu_write_32(CLP_SPI_HOST_REG_TXDATA, data); +} + +// configure_spi_host enables the IP and sets the timing behavior +void enable_spi_host() { + uint32_t read_data; + printf("Enabling spi_host\n"); + lsu_write_32(CLP_SPI_HOST_REG_CONTROL, + (1 << SPI_HOST_REG_CONTROL_SPIEN_LOW) | + (1 << SPI_HOST_REG_CONTROL_OUTPUT_EN_LOW) | + (0x7f << SPI_HOST_REG_CONTROL_RX_WATERMARK_LOW)); +} + +void configure_spi_host(int host) { + uint32_t offset; + + if (host == 0) { + offset = CLP_SPI_HOST_REG_CONFIGOPTS_0; + } else { + offset = CLP_SPI_HOST_REG_CONFIGOPTS_1; + } + + printf("Configuring spi_host[%d]\n", host); + lsu_write_32(offset, (0 << SPI_HOST_REG_CONFIGOPTS_0_CPOL_LOW) | + (0 << SPI_HOST_REG_CONFIGOPTS_0_CPHA_LOW) | + (0 << SPI_HOST_REG_CONFIGOPTS_0_FULLCYC_LOW) | + (0 << SPI_HOST_REG_CONFIGOPTS_0_CSNLEAD_LOW) | + (0 << SPI_HOST_REG_CONFIGOPTS_0_CSNTRAIL_LOW) | + (0 << SPI_HOST_REG_CONFIGOPTS_0_CSNIDLE_LOW) | + (0 << SPI_HOST_REG_CONFIGOPTS_0_CLKDIV_LOW)); +} + +//---------------------------------------------------------------- +// run_jedec_id_test() +// +// Configures the spi_host to request the jedec id +// The spiflash device will return 7 bytes of continuous code ('h7f) +// followed by the JedecId ('h1f) and the DeviceId ('h1234) +//---------------------------------------------------------------- +int run_jedec_id_test(int host) { + uint32_t status, rxqd, rx_data; + uint32_t exp_data[3]; + int error = 0, words; + + exp_data[0] = 0x7f7f7f7f; + exp_data[1] = 0x1f7f7f7f; + if (host == 0) { + exp_data[2] = 0xf10a; + } else { + exp_data[2] = 0xf10b; + } + + // Load the TX FIFO with instructions and data to be transmitted + write_tx_fifo(CmdJedecId); + + // Specify which device should receive the next command + set_spi_csid(host); + + // Issue speed, direction and length details for the next command + // segment. If a command consists of multiple segments, set csaat to one + // for all segments except the last one. + // + // Issue Command Instruction + spi_command(0, // length + 1 + 1, // csaat + Standard, // Speed + WrOnly // Direction + ); + + // spi flash will return 10 bytes for the jedec command + spi_command(9, // length + 1 + 0, // csaat + Standard, // Speed + RdOnly // Direction + ); + + // Wait for spi commands to finish before reading responses + spi_command_wait(); + + words = sizeof(exp_data) / 4; + error += fifo_rx_wait(words); + + printf(" - reading data from device...\n"); + for (int ii = 0; ii < words; ii += 1) { + error += read_and_compare(CLP_SPI_HOST_REG_RXDATA, exp_data[ii]); + } + + return error; +} + +//---------------------------------------------------------------- +// run_read_test() +// +// Configures the spi_host to request data from the spi flash +//---------------------------------------------------------------- +int run_read_test(int host) { + uint32_t status, rxqd, rx_data, exp_data; + uint32_t addr; + int error = 0; + int NumBytes = 256; + int SpiFlashAddr = 0x00ABCD; // 3B Address + + // Load the TX FIFO with instructions and data to be transmitted + write_tx_fifo(CmdReadQuad); + // Upper Bytes are transmitted first + write_tx_fifo((SpiFlashAddr & 0xff0000) >> 0 | (SpiFlashAddr & 0xff00) | + (SpiFlashAddr & 0xff) << 16); + + // Specify which device should receive the next command + set_spi_csid(host); + + // Issue speed, direction and length details for the next command + // segment. If a command consists of multiple segments, set csaat to one + // for all segments except the last one. + // + // Issue Command Instruction + spi_command(0, // length + 1 + 1, // csaat + Standard, // Speed + WrOnly); // Direction + // Issue 3 Byte Address - (Send the CmdEn4B if 4B is needed) + spi_command(2, // length + 1 + 1, // csaat + Standard, // Speed + WrOnly); // Direction + + // Issue 2 Dummy Cycles - This is derived from spiflash.DummyQuad-1 + spi_command(1, // length + 1 + 1, // csaat + Quad, // Speed + Dummy); // Direction + + // Request 13 bytes of data + spi_command(NumBytes - 1, // length + 1 + 0, // csaat + Quad, // Speed + RdOnly); // Direction + + // Wait for spi commands to finish before reading responses + spi_command_wait(); + + printf(" - reading data from device...\n"); + + error += fifo_rx_wait(NumBytes / 4); + + addr = SpiFlashAddr; + // Read and compare the bytes for comparison + for (int ii = 0; ii < NumBytes / 4; ii += 1) { + // calculate expected data + exp_data = addr & 0xff; + + // compare + error += read_and_compare(CLP_SPI_HOST_REG_RXDATA, + (exp_data + 3) << 24 | (exp_data + 2) << 16 | + (exp_data + 1) << 8 | (exp_data + 0) << 0); + addr += 4; + }; + return error; +} + +void main() { + int error; + + printf("---------------------------\n"); + printf(" QSPI Smoke Test \n"); + printf("---------------------------\n"); + + end_sim_if_qspi_disabled(); + enable_spi_host(); + + for (int host = 0; host < NUM_QSPI; host++) { + configure_spi_host(host); + error += run_jedec_id_test(host); + if (error > 0) printf("Error: %d\n", error); + error += run_read_test(host); + if (error > 0) printf("Error: %d\n", error); + error += run_read_test(host); + if (error > 0) printf("Error: %d\n", error); + error += run_read_test(host); + if (error > 0) printf("Error: %d\n", error); + } + + // End the sim in failure + if (error > 0) printf("%c", 0x1); +} diff --git a/src/integration/test_suites/smoke_test_qspi/smoke_test_qspi.yml b/src/integration/test_suites/smoke_test_qspi/smoke_test_qspi.yml new file mode 100644 index 000000000..3f60898d3 --- /dev/null +++ b/src/integration/test_suites/smoke_test_qspi/smoke_test_qspi.yml @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +--- +seed: 1 +testname: smoke_test_qspi diff --git a/src/integration/test_suites/smoke_test_trng/smoke_test_trng.c b/src/integration/test_suites/smoke_test_trng/smoke_test_trng.c index 84bd9a7fe..97c43b9a2 100644 --- a/src/integration/test_suites/smoke_test_trng/smoke_test_trng.c +++ b/src/integration/test_suites/smoke_test_trng/smoke_test_trng.c @@ -54,15 +54,16 @@ void poll_reg(uint32_t addr, uint32_t expected_data) { } void end_sim_if_itrng_disabled() { - uint32_t hw_cfg; - hw_cfg = lsu_read_32(CLP_SOC_IFC_REG_CPTRA_HW_CONFIG); - if (hw_cfg & SOC_IFC_REG_CPTRA_HW_CONFIG_ITRNG_EN_MASK) { - VPRINTF(LOW, "Internal TRNG is enabled, running TRNG smoke test\n"); - } else { - VPRINTF(FATAL, "Internal TRNG is not enabled, skipping TRNG smoke test\n"); - SEND_STDOUT_CTRL(0xFF); - while(1); - } + uint32_t hw_cfg; + hw_cfg = lsu_read_32(CLP_SOC_IFC_REG_CPTRA_HW_CONFIG); + if (hw_cfg & SOC_IFC_REG_CPTRA_HW_CONFIG_ITRNG_EN_MASK) { + VPRINTF(LOW, "Internal TRNG is enabled, running TRNG smoke test\n"); + } else { + VPRINTF(FATAL, "Internal TRNG is not enabled, skipping TRNG smoke test\n"); + SEND_STDOUT_CTRL(0xFF); + while (1) + ; + } } void enable_csrng() { diff --git a/src/integration/test_suites/smoke_test_uart/caliptra_isr.h b/src/integration/test_suites/smoke_test_uart/caliptra_isr.h new file mode 100644 index 000000000..23b511c4b --- /dev/null +++ b/src/integration/test_suites/smoke_test_uart/caliptra_isr.h @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// --------------------------------------------------------------------- +// File: caliptra_isr.h +// Description: +// Provides function declarations for use by external test files, so +// that the ISR functionality may behave like a library. +// TODO: +// This header file includes inline function definitions for event and +// test specific interrupt service behavior, so it should be copied and +// modified for each test. +// --------------------------------------------------------------------- + +#ifndef CALIPTRA_ISR_H + #define CALIPTRA_ISR_H + +#define EN_ISR_PRINTS 1 + +#include "caliptra_defines.h" +#include +#include "printf.h" + +//extern volatile uint32_t hmac_intr_status; + +////////////////////////////////////////////////////////////////////////////// +// Function Declarations +// +// Performs all the CSR setup to configure and enable vectored external interrupts +void init_interrupts(void); + +// These inline functions are used to insert event-specific functionality into the +// otherwise generic ISR that gets laid down by the parameterized macro "nonstd_swerv_isr" +inline void service_doe_error_intr() {printf("ERROR");} +inline void service_doe_notif_intr() {printf("ERROR");} +inline void service_ecc_error_intr () {printf("ERROR");} +inline void service_ecc_notif_intr () {printf("ERROR");} +inline void service_hmac_error_intr () {printf("ERROR");} +inline void service_hmac_notif_intr () {printf("ERROR");} + +inline void service_kv_error_intr () {printf("ERROR");} +inline void service_kv_notif_intr () {printf("ERROR");} +inline void service_sha512_error_intr() {printf("ERROR");} +inline void service_sha512_notif_intr() {printf("ERROR");} +inline void service_sha256_error_intr() {printf("ERROR");} +inline void service_sha256_notif_intr() {printf("ERROR");} +inline void service_qspi_error_intr () {printf("ERROR");} +inline void service_qspi_notif_intr () {printf("ERROR");} +inline void service_uart_error_intr () {printf("ERROR");} +inline void service_uart_notif_intr () {printf("ERROR");} +inline void service_i3c_error_intr () {printf("ERROR");} +inline void service_i3c_notif_intr () {printf("ERROR");} +inline void service_soc_ifc_error_intr () {printf("ERROR");} +inline void service_soc_ifc_notif_intr () {printf("ERROR");} +inline void service_sha512_acc_error_intr() {printf("ERROR");} +inline void service_sha512_acc_notif_intr() {printf("ERROR");} + + +#endif //CALIPTRA_ISR_H diff --git a/src/integration/test_suites/smoke_test_uart/smoke_test_uart.c b/src/integration/test_suites/smoke_test_uart/smoke_test_uart.c new file mode 100644 index 000000000..b5c97eb38 --- /dev/null +++ b/src/integration/test_suites/smoke_test_uart/smoke_test_uart.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include +#include + +#include "caliptra_defines.h" +#include "caliptra_isr.h" +#include "printf.h" +#include "riscv-csr.h" +#include "riscv_hw_if.h" + +volatile char *stdout = (char *)STDOUT; +volatile uint32_t intr_count = 0; +volatile uint32_t rst_count __attribute__((section(".dccm.persistent"))) = 0; +#ifdef CPT_VERBOSITY +enum printf_verbosity verbosity_g = CPT_VERBOSITY; +#else +enum printf_verbosity verbosity_g = LOW; +#endif + +void end_sim_if_uart_disabled() { + uint32_t hw_cfg; + hw_cfg = lsu_read_32(CLP_SOC_IFC_REG_CPTRA_HW_CONFIG); + if (hw_cfg & SOC_IFC_REG_CPTRA_HW_CONFIG_UART_EN_MASK) { + VPRINTF(LOW, "Internal UART is enabled, running UART smoke test\n"); + } else { + VPRINTF(FATAL, "Internal UART is not enabled, skipping UART smoke test\n"); + SEND_STDOUT_CTRL(0xFF); + while (1) + ; + } +} + +void uart_tx(uint8_t data) { + uint32_t status, tx_full, wdata; + printf("uart_tx >> Sending 0x%x\n", data); + // Check the TX fifo is not full + do { + status = lsu_read_32(CLP_UART_STATUS); + tx_full = ((status & UART_STATUS_TXFULL_MASK) >> UART_STATUS_TXFULL_LOW); + } while (tx_full); + + wdata = data; + lsu_write_32(CLP_UART_WDATA, wdata); +} + +uint8_t uart_rx() { + uint32_t status, rx_empty, data; + uint8_t rdata; + + // Check the RX Empty + do { + status = lsu_read_32(CLP_UART_STATUS); + rx_empty = ((status & UART_STATUS_RXEMPTY_MASK) >> UART_STATUS_RXEMPTY_LOW); + } while (rx_empty); + + // read the data + data = lsu_read_32(CLP_UART_RDATA); + printf("uart_rx << Receiving 0x%x\n", data); + rdata = data & 0xff; + return rdata; +} + +// enable uart tx and rx +void enable_uart() { + uint64_t nco, baud_rate, ip_frequency; + uint32_t ctrl; + + baud_rate = 115200; + ip_frequency = 100000000; // 100 MHz + + // 31:16 NCO + // 9:8 rxblvl + // 7 parity_odd + // 6 parity_even + // 5 line loopback enable + // 4 system loopback enable + // 2 RX Noise Filter + // 1 Rx Enable + // 0 Tx Enable + // NCO Equation: 2^20 * Fbaud + // -------------- + // Fclk + // Fbaud = baud rate in bits per second + // Fclk = fixed frequency of the IP + nco = baud_rate << 20; + nco = nco / ip_frequency; + printf("nco = %d\n", nco); + + ctrl = ((nco & 0xffff) << UART_CTRL_NCO_LOW) | UART_CTRL_TX_MASK | + UART_CTRL_RX_MASK; + lsu_write_32(CLP_UART_CTRL, ctrl); // Enable RX/TX +} + +//---------------------------------------------------------------- +// run_loopback_test() +// +// This is a simple test that writes data and checks that data was received. +// TODO: add threads to process read/write in parallel (or interrupts), but also +// add a real uart driver at the top level instead of a loopback wire. +//---------------------------------------------------------------- +int run_loopback_test() { + int error = 0; + uint8_t rxdata, txdata; + + for (int ii = 0; ii < 10; ii++) { + txdata = 3 * ii + 7; + uart_tx(txdata); + + rxdata = uart_rx(); + + if (rxdata != txdata) { + printf("run_loopback_test: Got: 0x%x Want: 0x%x\n", rxdata, txdata); + error += 1; + } + } + + return error; +} + +void main() { + int error; + + printf("---------------------------\n"); + printf(" UART Smoke Test \n"); + printf("---------------------------\n"); + + end_sim_if_uart_disabled(); + enable_uart(); + + error += run_loopback_test(); + if (error > 0) printf("Error: %d\n", error); + + // End the sim in failure + if (error > 0) printf("%c", 0x1); +} diff --git a/src/integration/test_suites/smoke_test_uart/smoke_test_uart.yml b/src/integration/test_suites/smoke_test_uart/smoke_test_uart.yml new file mode 100644 index 000000000..e252d0c56 --- /dev/null +++ b/src/integration/test_suites/smoke_test_uart/smoke_test_uart.yml @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +--- +seed: 1 +testname: smoke_test_uart diff --git a/src/prim/config/compile.yml b/src/prim/config/compile.yml index 28693eb96..ae69e7735 100644 --- a/src/prim/config/compile.yml +++ b/src/prim/config/compile.yml @@ -33,6 +33,7 @@ targets: directories: [$COMPILE_ROOT/rtl] files: # - $COMPILE_ROOT/rtl/prim_flop_macros.sv + - $COMPILE_ROOT/rtl/prim_flop_en.sv - $COMPILE_ROOT/rtl/prim_flop_2sync.sv - $COMPILE_ROOT/rtl/prim_lfsr.sv - $COMPILE_ROOT/rtl/prim_mubi4_sync.sv diff --git a/src/prim/config/prim.vf b/src/prim/config/prim.vf index cc5908348..e87eac6c4 100644 --- a/src/prim/config/prim.vf +++ b/src/prim/config/prim.vf @@ -26,6 +26,7 @@ ${CALIPTRA_ROOT}/src/lc_ctrl/rtl/lc_ctrl_pkg.sv ${CALIPTRA_ROOT}/src/libs/rtl/ahb_to_reg_adapter.sv ${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_flop.sv ${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_buf.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_flop_en.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_flop_2sync.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_lfsr.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_mubi4_sync.sv @@ -54,4 +55,4 @@ ${CALIPTRA_ROOT}/src/prim/rtl/prim_fifo_sync.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_arbiter_ppc.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_sum_tree.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_subreg_ext.sv -${CALIPTRA_ROOT}/src/prim/rtl/prim_edge_detector.sv \ No newline at end of file +${CALIPTRA_ROOT}/src/prim/rtl/prim_edge_detector.sv diff --git a/src/prim/rtl/prim_flop_en.sv b/src/prim/rtl/prim_flop_en.sv new file mode 100644 index 000000000..8f9ba1a10 --- /dev/null +++ b/src/prim/rtl/prim_flop_en.sv @@ -0,0 +1,53 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This file is auto-generated. +// Used parser: Fallback (regex) + +`ifndef PRIM_DEFAULT_IMPL + `define PRIM_DEFAULT_IMPL prim_pkg::ImplGeneric +`endif + +// This is to prevent AscentLint warnings in the generated +// abstract prim wrapper. These warnings occur due to the .* +// use. TODO: we may want to move these inline waivers +// into a separate, generated waiver file for consistency. +//ri lint_check_off OUTPUT_NOT_DRIVEN INPUT_NOT_READ HIER_BRANCH_NOT_READ +module prim_flop_en + +#( + + parameter int Width = 1, + parameter bit EnSecBuf = 0, + parameter logic [Width-1:0] ResetValue = 0 + +) ( + input clk_i, + input rst_ni, + input en_i, + input [Width-1:0] d_i, + output logic [Width-1:0] q_o +); + parameter prim_pkg::impl_e Impl = `PRIM_DEFAULT_IMPL; + +if (Impl == prim_pkg::ImplXilinx) begin : gen_xilinx + prim_xilinx_flop_en #( + .EnSecBuf(EnSecBuf), + .ResetValue(ResetValue), + .Width(Width) + ) u_impl_xilinx ( + .* + ); +end else begin : gen_generic + prim_generic_flop_en #( + .EnSecBuf(EnSecBuf), + .ResetValue(ResetValue), + .Width(Width) + ) u_impl_generic ( + .* + ); +end + +endmodule +//ri lint_check_on OUTPUT_NOT_DRIVEN INPUT_NOT_READ HIER_BRANCH_NOT_READ diff --git a/src/prim_generic/config/compile.yml b/src/prim_generic/config/compile.yml index bda50db3c..16a806228 100644 --- a/src/prim_generic/config/compile.yml +++ b/src/prim_generic/config/compile.yml @@ -5,5 +5,6 @@ targets: rtl: directories: [$COMPILE_ROOT/rtl] files: + - $COMPILE_ROOT/rtl/prim_generic_flop_en.sv - $COMPILE_ROOT/rtl/prim_generic_flop.sv - $COMPILE_ROOT/rtl/prim_generic_buf.sv diff --git a/src/prim_generic/config/prim_generic.vf b/src/prim_generic/config/prim_generic.vf index 8f9818297..844abb8b8 100644 --- a/src/prim_generic/config/prim_generic.vf +++ b/src/prim_generic/config/prim_generic.vf @@ -1,3 +1,4 @@ +incdir+${CALIPTRA_ROOT}/src/prim_generic/rtl +${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_flop_en.sv ${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_flop.sv -${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_buf.sv \ No newline at end of file +${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_buf.sv diff --git a/src/prim_generic/rtl/prim_generic_flop_en.sv b/src/prim_generic/rtl/prim_generic_flop_en.sv new file mode 100644 index 000000000..3c367b735 --- /dev/null +++ b/src/prim_generic/rtl/prim_generic_flop_en.sv @@ -0,0 +1,39 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +module prim_generic_flop_en #( + parameter int Width = 1, + parameter bit EnSecBuf = 0, + parameter logic [Width-1:0] ResetValue = 0 +) ( + input clk_i, + input rst_ni, + input en_i, + input [Width-1:0] d_i, + output logic [Width-1:0] q_o +); + + logic en; + if (EnSecBuf) begin : gen_en_sec_buf + prim_sec_anchor_buf #( + .Width(1) + ) u_en_buf ( + .in_i(en_i), + .out_o(en) + ); + end else begin : gen_en_no_sec_buf + assign en = en_i; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + q_o <= ResetValue; + end else if (en) begin + q_o <= d_i; + end + end + +endmodule diff --git a/src/riscv_core/veer_el2/rtl/dec/el2_dec_tlu_ctl.sv b/src/riscv_core/veer_el2/rtl/dec/el2_dec_tlu_ctl.sv index 511c0adaa..0e753606f 100644 --- a/src/riscv_core/veer_el2/rtl/dec/el2_dec_tlu_ctl.sv +++ b/src/riscv_core/veer_el2/rtl/dec/el2_dec_tlu_ctl.sv @@ -2788,7 +2788,9 @@ assign dec_csr_rddata_d[31:0] = ( ({32{csr_misa}} & 32'h40001104) | endmodule // el2_dec_tlu_ctl -module el2_dec_timer_ctl #( +module el2_dec_timer_ctl +import el2_pkg::*; +#( `include "el2_param.vh" ) ( diff --git a/src/riscv_core/veer_el2/rtl/el2_dma_ctrl.sv b/src/riscv_core/veer_el2/rtl/el2_dma_ctrl.sv index b415a8846..1398a10bd 100644 --- a/src/riscv_core/veer_el2/rtl/el2_dma_ctrl.sv +++ b/src/riscv_core/veer_el2/rtl/el2_dma_ctrl.sv @@ -21,7 +21,9 @@ // //******************************************************************************** -module el2_dma_ctrl #( +module el2_dma_ctrl +import el2_pkg::*; +#( `include "el2_param.vh" )( input logic clk, diff --git a/src/riscv_core/veer_el2/rtl/el2_pic_ctrl.sv b/src/riscv_core/veer_el2/rtl/el2_pic_ctrl.sv index 8f8881f84..797872e15 100644 --- a/src/riscv_core/veer_el2/rtl/el2_pic_ctrl.sv +++ b/src/riscv_core/veer_el2/rtl/el2_pic_ctrl.sv @@ -20,7 +20,9 @@ // Comments: //******************************************************************************** -module el2_pic_ctrl #( +module el2_pic_ctrl +import el2_pkg::*; +#( `include "el2_param.vh" ) ( diff --git a/src/riscv_core/veer_el2/rtl/ifu/el2_ifu.sv b/src/riscv_core/veer_el2/rtl/ifu/el2_ifu.sv index 837a9c249..d36cd68bd 100644 --- a/src/riscv_core/veer_el2/rtl/ifu/el2_ifu.sv +++ b/src/riscv_core/veer_el2/rtl/ifu/el2_ifu.sv @@ -241,6 +241,26 @@ import el2_pkg::*; logic [1:0] [$clog2(pt.BTB_SIZE)-1:0] ifu_bp_fa_index_f; + logic [1:0] ic_fetch_val_f; + logic [31:0] ic_data_f; + logic [31:0] ifu_fetch_data_f; + logic ifc_fetch_req_f; + logic ifc_fetch_req_f_raw; + logic [1:0] iccm_rd_ecc_double_err; // This fetch has an iccm double error. + + logic ifu_async_error_start; + + + assign ifu_fetch_data_f[31:0] = ic_data_f[31:0]; + assign ifu_fetch_val[1:0] = ic_fetch_val_f[1:0]; + assign ifu_fetch_pc[31:1] = ifc_fetch_addr_f[31:1]; + + logic ifc_fetch_uncacheable_bf; // The fetch request is uncacheable space. BF stage + logic ifc_fetch_req_bf; // Fetch request. Comes with the address. BF stage + logic ifc_fetch_req_bf_raw; // Fetch request without some qualifications. Used for clock-gating. BF stage + logic ifc_iccm_access_bf; // This request is to the ICCM. Do not generate misses to the bus. + logic ifc_region_acc_fault_bf; // Access fault. in ICCM region but offset is outside defined ICCM. + // fetch control el2_ifu_ifc_ctl #(.pt(pt)) ifc (.* ); @@ -262,25 +282,6 @@ import el2_pkg::*; end - logic [1:0] ic_fetch_val_f; - logic [31:0] ic_data_f; - logic [31:0] ifu_fetch_data_f; - logic ifc_fetch_req_f; - logic ifc_fetch_req_f_raw; - logic [1:0] iccm_rd_ecc_double_err; // This fetch has an iccm double error. - - logic ifu_async_error_start; - - - assign ifu_fetch_data_f[31:0] = ic_data_f[31:0]; - assign ifu_fetch_val[1:0] = ic_fetch_val_f[1:0]; - assign ifu_fetch_pc[31:1] = ifc_fetch_addr_f[31:1]; - - logic ifc_fetch_uncacheable_bf; // The fetch request is uncacheable space. BF stage - logic ifc_fetch_req_bf; // Fetch request. Comes with the address. BF stage - logic ifc_fetch_req_bf_raw; // Fetch request without some qualifications. Used for clock-gating. BF stage - logic ifc_iccm_access_bf; // This request is to the ICCM. Do not generate misses to the bus. - logic ifc_region_acc_fault_bf; // Access fault. in ICCM region but offset is outside defined ICCM. // aligner diff --git a/src/riscv_core/veer_el2/rtl/include/el2_def.sv b/src/riscv_core/veer_el2/rtl/include/el2_def.sv index 5292ce49d..a1b239b57 100644 --- a/src/riscv_core/veer_el2/rtl/include/el2_def.sv +++ b/src/riscv_core/veer_el2/rtl/include/el2_def.sv @@ -21,6 +21,8 @@ //`define EL2_DEF_SV package el2_pkg; +`include "el2_pdef.vh" + typedef struct packed { logic trace_rv_i_valid_ip; logic [31:0] trace_rv_i_insn_ip; diff --git a/src/riscv_core/veer_el2/rtl/lib/el2_lib.sv b/src/riscv_core/veer_el2/rtl/lib/el2_lib.sv index 0ac587479..d71acf06e 100644 --- a/src/riscv_core/veer_el2/rtl/lib/el2_lib.sv +++ b/src/riscv_core/veer_el2/rtl/lib/el2_lib.sv @@ -15,7 +15,9 @@ // limitations under the License. //******************************************************************************** -module el2_btb_tag_hash #( +module el2_btb_tag_hash +import el2_pkg::*; +#( `include "el2_param.vh" ) ( input logic [pt.BTB_ADDR_HI+pt.BTB_BTAG_SIZE+pt.BTB_BTAG_SIZE+pt.BTB_BTAG_SIZE:pt.BTB_ADDR_HI+1] pc, @@ -27,7 +29,9 @@ module el2_btb_tag_hash #( pc[pt.BTB_ADDR_HI+pt.BTB_BTAG_SIZE:pt.BTB_ADDR_HI+1])}; endmodule -module el2_btb_tag_hash_fold #( +module el2_btb_tag_hash_fold +import el2_pkg::*; +#( `include "el2_param.vh" )( input logic [pt.BTB_ADDR_HI+pt.BTB_BTAG_SIZE+pt.BTB_BTAG_SIZE:pt.BTB_ADDR_HI+1] pc, @@ -40,7 +44,9 @@ module el2_btb_tag_hash_fold #( endmodule -module el2_btb_addr_hash #( +module el2_btb_addr_hash +import el2_pkg::*; +#( `include "el2_param.vh" )( input logic [pt.BTB_INDEX3_HI:pt.BTB_INDEX1_LO] pc, @@ -60,7 +66,9 @@ end endmodule -module el2_btb_ghr_hash #( +module el2_btb_ghr_hash +import el2_pkg::*; +#( `include "el2_param.vh" )( input logic [pt.BTB_ADDR_HI:pt.BTB_ADDR_LO] hashin, diff --git a/src/soc_ifc/rtl/caliptra_top_reg.h b/src/soc_ifc/rtl/caliptra_top_reg.h index 57af665ca..90c555aa1 100644 --- a/src/soc_ifc/rtl/caliptra_top_reg.h +++ b/src/soc_ifc/rtl/caliptra_top_reg.h @@ -15,7 +15,6 @@ #ifndef CALIPTRA_TOP_REG_HEADER #define CALIPTRA_TOP_REG_HEADER - #define CALIPTRA_TOP_REG_BASE_ADDR (0x0) #define CALIPTRA_TOP_REG_MBOX_CSR_BASE_ADDR (0x30020000) #define CALIPTRA_TOP_REG_MBOX_CSR_MBOX_LOCK (0x30020000) @@ -476,4 +475,5 @@ #define GENERIC_AND_FUSE_REG_FUSE_LIFE_CYCLE_LIFE_CYCLE_MASK (0x3) -#endif \ No newline at end of file +#endif + diff --git a/src/soc_ifc/rtl/caliptra_top_reg_defines.svh b/src/soc_ifc/rtl/caliptra_top_reg_defines.svh index 1357fd775..4eb915d3e 100644 --- a/src/soc_ifc/rtl/caliptra_top_reg_defines.svh +++ b/src/soc_ifc/rtl/caliptra_top_reg_defines.svh @@ -278,6 +278,8 @@ `define GENERIC_AND_FUSE_REG_CPTRA_HW_CONFIG_QSPI_EN_MASK (32'h2) `define GENERIC_AND_FUSE_REG_CPTRA_HW_CONFIG_I3C_EN_LOW (2) `define GENERIC_AND_FUSE_REG_CPTRA_HW_CONFIG_I3C_EN_MASK (32'h4) +`define GENERIC_AND_FUSE_REG_CPTRA_HW_CONFIG_UART_EN_LOW (3) +`define GENERIC_AND_FUSE_REG_CPTRA_HW_CONFIG_UART_EN_MASK (32'h8) `define CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_WDT_TIMER1_EN (32'h300300e0) `define GENERIC_AND_FUSE_REG_CPTRA_WDT_TIMER1_EN (32'he0) `define GENERIC_AND_FUSE_REG_CPTRA_WDT_TIMER1_EN_TIMER1_EN_LOW (0) @@ -476,4 +478,4 @@ `define GENERIC_AND_FUSE_REG_FUSE_LIFE_CYCLE_LIFE_CYCLE_MASK (32'h3) -`endif \ No newline at end of file +`endif diff --git a/src/soc_ifc/rtl/soc_ifc_external_reg.rdl b/src/soc_ifc/rtl/soc_ifc_external_reg.rdl index 73782fdda..4e6e12a98 100644 --- a/src/soc_ifc/rtl/soc_ifc_external_reg.rdl +++ b/src/soc_ifc/rtl/soc_ifc_external_reg.rdl @@ -264,6 +264,7 @@ reg { field {sw=r; hw=w;} iTRNG_en; field {sw=r; hw=w;} QSPI_en; field {sw=r; hw=w;} I3C_en; + field {sw=r; hw=w;} UART_en; } CPTRA_HW_CONFIG; //Timer1 diff --git a/src/soc_ifc/rtl/soc_ifc_reg.sv b/src/soc_ifc/rtl/soc_ifc_reg.sv index 61b553372..88e8b9e8b 100644 --- a/src/soc_ifc/rtl/soc_ifc_reg.sv +++ b/src/soc_ifc/rtl/soc_ifc_reg.sv @@ -4247,7 +4247,8 @@ module soc_ifc_reg ( assign readback_array[55][0:0] = (decoded_reg_strb.CPTRA_HW_CONFIG && !decoded_req_is_wr) ? hwif_in.CPTRA_HW_CONFIG.iTRNG_en.next : '0; assign readback_array[55][1:1] = (decoded_reg_strb.CPTRA_HW_CONFIG && !decoded_req_is_wr) ? hwif_in.CPTRA_HW_CONFIG.QSPI_en.next : '0; assign readback_array[55][2:2] = (decoded_reg_strb.CPTRA_HW_CONFIG && !decoded_req_is_wr) ? hwif_in.CPTRA_HW_CONFIG.I3C_en.next : '0; - assign readback_array[55][31:3] = '0; + assign readback_array[55][3:3] = (decoded_reg_strb.CPTRA_HW_CONFIG && !decoded_req_is_wr) ? hwif_in.CPTRA_HW_CONFIG.UART_en.next : '0; + assign readback_array[55][31:4] = '0; assign readback_array[56][0:0] = (decoded_reg_strb.CPTRA_WDT_TIMER1_EN && !decoded_req_is_wr) ? field_storage.CPTRA_WDT_TIMER1_EN.timer1_en.value : '0; assign readback_array[56][31:1] = '0; assign readback_array[57][0:0] = (decoded_reg_strb.CPTRA_WDT_TIMER1_CTRL && !decoded_req_is_wr) ? field_storage.CPTRA_WDT_TIMER1_CTRL.timer1_restart.value : '0; @@ -4393,4 +4394,6 @@ module soc_ifc_reg ( assign cpuif_rd_ack = readback_done; assign cpuif_rd_data = readback_data; assign cpuif_rd_err = readback_err; -endmodule \ No newline at end of file + +endmodule + diff --git a/src/soc_ifc/rtl/soc_ifc_reg_pkg.sv b/src/soc_ifc/rtl/soc_ifc_reg_pkg.sv index b1c3a8f3e..bda73281f 100644 --- a/src/soc_ifc/rtl/soc_ifc_reg_pkg.sv +++ b/src/soc_ifc/rtl/soc_ifc_reg_pkg.sv @@ -163,10 +163,15 @@ package soc_ifc_reg_pkg; logic next; } soc_ifc_reg__CPTRA_HW_CONFIG__I3C_en__in_t; + typedef struct packed{ + logic next; + } soc_ifc_reg__CPTRA_HW_CONFIG__UART_en__in_t; + typedef struct packed{ soc_ifc_reg__CPTRA_HW_CONFIG__iTRNG_en__in_t iTRNG_en; soc_ifc_reg__CPTRA_HW_CONFIG__QSPI_en__in_t QSPI_en; soc_ifc_reg__CPTRA_HW_CONFIG__I3C_en__in_t I3C_en; + soc_ifc_reg__CPTRA_HW_CONFIG__UART_en__in_t UART_en; } soc_ifc_reg__CPTRA_HW_CONFIG__in_t; typedef struct packed{ diff --git a/src/soc_ifc/rtl/soc_ifc_reg_uvm.sv b/src/soc_ifc/rtl/soc_ifc_reg_uvm.sv index 9aa564480..e85e404c5 100644 --- a/src/soc_ifc/rtl/soc_ifc_reg_uvm.sv +++ b/src/soc_ifc/rtl/soc_ifc_reg_uvm.sv @@ -400,6 +400,7 @@ package soc_ifc_reg_uvm; rand uvm_reg_field iTRNG_en; rand uvm_reg_field QSPI_en; rand uvm_reg_field I3C_en; + rand uvm_reg_field UART_en; function new(string name = "soc_ifc_reg__CPTRA_HW_CONFIG"); super.new(name, 32, UVM_NO_COVERAGE); @@ -412,6 +413,8 @@ package soc_ifc_reg_uvm; this.QSPI_en.configure(this, 1, 1, "RO", 1, 'h0, 0, 1, 0); this.I3C_en = new("I3C_en"); this.I3C_en.configure(this, 1, 2, "RO", 1, 'h0, 0, 1, 0); + this.UART_en = new("UART_en"); + this.UART_en.configure(this, 1, 3, "RO", 1, 'h0, 0, 1, 0); endfunction : build endclass : soc_ifc_reg__CPTRA_HW_CONFIG diff --git a/src/soc_ifc/rtl/soc_ifc_top.sv b/src/soc_ifc/rtl/soc_ifc_top.sv index 3195fb753..4347ec15a 100644 --- a/src/soc_ifc/rtl/soc_ifc_top.sv +++ b/src/soc_ifc/rtl/soc_ifc_top.sv @@ -372,8 +372,17 @@ always_comb soc_ifc_reg_hwif_in.CPTRA_HW_CONFIG.iTRNG_en.next = 1'b1; `else always_comb soc_ifc_reg_hwif_in.CPTRA_HW_CONFIG.iTRNG_en.next = 1'b0; `endif +`ifdef CALIPTRA_INTERNAL_QSPI +always_comb soc_ifc_reg_hwif_in.CPTRA_HW_CONFIG.QSPI_en.next = 1'b1; +`else always_comb soc_ifc_reg_hwif_in.CPTRA_HW_CONFIG.QSPI_en.next = 1'b0; +`endif always_comb soc_ifc_reg_hwif_in.CPTRA_HW_CONFIG.I3C_en.next = 1'b0; +`ifdef CALIPTRA_INTERNAL_UART +always_comb soc_ifc_reg_hwif_in.CPTRA_HW_CONFIG.UART_en.next = 1'b1; +`else +always_comb soc_ifc_reg_hwif_in.CPTRA_HW_CONFIG.UART_en.next = 1'b0; +`endif always_comb begin for (int i = 0; i < `CLP_OBF_KEY_DWORDS; i++) begin @@ -707,11 +716,9 @@ assign timer2_en = soc_ifc_reg_hwif_out.CPTRA_WDT_TIMER2_EN.timer2_en; assign timer1_restart = soc_ifc_reg_hwif_out.CPTRA_WDT_TIMER1_CTRL.timer1_restart; assign timer2_restart = soc_ifc_reg_hwif_out.CPTRA_WDT_TIMER2_CTRL.timer2_restart; -always_comb begin - for (int i = 0; i < WDT_TIMEOUT_PERIOD_NUM_DWORDS; i++) begin - timer1_timeout_period[i] = soc_ifc_reg_hwif_out.CPTRA_WDT_TIMER1_TIMEOUT_PERIOD[i].timer1_timeout_period.value; - timer2_timeout_period[i] = soc_ifc_reg_hwif_out.CPTRA_WDT_TIMER2_TIMEOUT_PERIOD[i].timer2_timeout_period.value; - end +for (genvar i = 0; i < WDT_TIMEOUT_PERIOD_NUM_DWORDS; i++) begin + assign timer1_timeout_period[i] = soc_ifc_reg_hwif_out.CPTRA_WDT_TIMER1_TIMEOUT_PERIOD[i].timer1_timeout_period.value; + assign timer2_timeout_period[i] = soc_ifc_reg_hwif_out.CPTRA_WDT_TIMER2_TIMEOUT_PERIOD[i].timer2_timeout_period.value; end //Set WDT status reg diff --git a/src/spi_host/config/compile.yml b/src/spi_host/config/compile.yml new file mode 100644 index 000000000..5f7914b0e --- /dev/null +++ b/src/spi_host/config/compile.yml @@ -0,0 +1,26 @@ +--- +provides: [spi_host] +schema_version: 2.4.0 +requires: + - libs + - prim +targets: + rtl: + directories: [$COMPILE_ROOT/rtl] + files: + - $COMPILE_ROOT/rtl/spi_host_reg_pkg.sv + - $COMPILE_ROOT/rtl/spi_host_cmd_pkg.sv + - $COMPILE_ROOT/rtl/spi_host.sv + - $COMPILE_ROOT/rtl/spi_host_byte_merge.sv + - $COMPILE_ROOT/rtl/spi_host_byte_select.sv + - $COMPILE_ROOT/rtl/spi_host_command_queue.sv + - $COMPILE_ROOT/rtl/spi_host_core.sv + - $COMPILE_ROOT/rtl/spi_host_data_fifos.sv + - $COMPILE_ROOT/rtl/spi_host_fsm.sv + - $COMPILE_ROOT/rtl/spi_host_reg_top.sv + - $COMPILE_ROOT/rtl/spi_host_shift_register.sv + tops: [spi_host] + tb: + directories: [$COMPILE_ROOT/tb] + files: + - $COMPILE_ROOT/tb/spi_host_tb.sv diff --git a/src/spi_host/config/spi_host.vf b/src/spi_host/config/spi_host.vf new file mode 100644 index 000000000..246a36c47 --- /dev/null +++ b/src/spi_host/config/spi_host.vf @@ -0,0 +1,58 @@ ++incdir+${CALIPTRA_ROOT}/src/libs/rtl ++incdir+${CALIPTRA_ROOT}/src/prim/rtl +${CALIPTRA_ROOT}/src/libs/rtl/ahb_defines_pkg.sv +${CALIPTRA_ROOT}/src/libs/rtl/ahb_slv_sif.sv +${CALIPTRA_ROOT}/src/libs/rtl/ahb_to_reg_adapter.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_util_pkg.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_alert_pkg.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_subreg_pkg.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_mubi_pkg.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_cipher_pkg.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_pkg.sv +${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_buf.sv +${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_flop.sv +${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_flop_en.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_flop_en.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_flop_2sync.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_lfsr.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_mubi4_sync.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_diff_decode.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_sec_anchor_buf.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_slicer.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_count.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_sparse_fsm_flop.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_dom_and_2share.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_sec_anchor_flop.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_reg_we_check.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_packer_fifo.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_max_tree.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_subreg_arb.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_subreg.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_intr_hw.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_onehot_check.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_mubi8_sync.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_fifo_sync_cnt.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_buf.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_alert_receiver.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_flop.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_alert_sender.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_fifo_sync.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_arbiter_ppc.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_sum_tree.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_subreg_ext.sv +${CALIPTRA_ROOT}/src/prim/rtl/prim_edge_detector.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_reg_pkg.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_cmd_pkg.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_byte_merge.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_byte_select.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_command_queue.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_core.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_data_fifos.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_fsm.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_reg_top.sv +${CALIPTRA_ROOT}/src/spi_host/rtl/spi_host_shift_register.sv +${CALIPTRA_ROOT}/src/spi_host/tb/spi_device_pkg.sv +${CALIPTRA_ROOT}/src/spi_host/tb/spiflash.sv +${CALIPTRA_ROOT}/src/spi_host/tb/spi_host_tb.sv + diff --git a/src/spi_host/config/spi_host.vlt b/src/spi_host/config/spi_host.vlt new file mode 100644 index 000000000..9f196f260 --- /dev/null +++ b/src/spi_host/config/spi_host.vlt @@ -0,0 +1,3 @@ +${CALIPTRA_ROOT}/src/prim/lint/prim_max_tree.vlt +${CALIPTRA_ROOT}/src/prim/lint/prim_sum_tree.vlt +${CALIPTRA_ROOT}/src/prim/lint/prim_onehot_check.vlt diff --git a/src/spi_host/data/spi_host.hjson b/src/spi_host/data/spi_host.hjson index 211dfa4d9..9aca33367 100644 --- a/src/spi_host/data/spi_host.hjson +++ b/src/spi_host/data/spi_host.hjson @@ -32,7 +32,7 @@ { name: "NumCS", desc: "The number of active-low chip select (cs_n) lines to create.", type: "int", - default: "1" + default: "2" }, { name: "TxDepth", desc: "The size of the Tx FIFO (in words)", @@ -381,56 +381,66 @@ // Exclude from RW tests "excl:CsrAllTests:CsrExclWrite"] }, - { window: { - name: "RXDATA", - items: "1", - validbits: "32", - desc: '''SPI Receive Data. - Reads from this window pull data from the RXFIFO. + { name: "RXDATA", + desc: '''SPI Receive Data - The serial order of bit transmission - is chosen to match SPI flash devices. Individual bytes - are always transmitted with the most significant bit first. - Only four-bute reads are supported. If ByteOrder = 0, - the first byte received is packed in the MSB of !!RXDATA. - For some processor architectures, this could lead to shuffling - of flash data as compared to how it is written in memory. - In which case, choosing ByteOrder = 1 can reverse the - byte-order of each data read, causing the first byte - received to be packed into the LSB of !!RXDATA. (Though within - each byte the most significant bit is always pulled - from the bus first.) - ''' - swaccess: "ro", - } + Reads from this window pull data from the RXFIFO. + + The serial order of bit transmission + is chosen to match SPI flash devices. Individual bytes + are always transmitted with the most significant bit first. + Only four-bute reads are supported. If ByteOrder = 0, + the first byte received is packed in the MSB of !!RXDATA. + For some processor architectures, this could lead to shuffling + of flash data as compared to how it is written in memory. + In which case, choosing ByteOrder = 1 can reverse the + byte-order of each data read, causing the first byte + received to be packed into the LSB of !!RXDATA. (Though within + each byte the most significant bit is always pulled + from the bus first.) + ''' + swaccess: "ro", + hwaccess: "hwo", + fields: [ + { bits: "31:0", + name: "RXDATA", + desc: "SPI Receive Data", + resval: "0x0" + } + ] }, - { window: { - name: "TXDATA", - items: "1", - validbits: "32", - byte-write: "true", - desc: '''SPI Transmit Data. - Data written to this window is placed into the TXFIFO. - Byte-enables are supported for writes. + { name: "TXDATA", + desc: '''SPI Transmit Data + Data written to this window is placed into the TXFIFO. + Byte-enables are supported for writes. - The serial order of bit transmission - is chosen to match SPI flash devices. Individual bytes - are always transmitted with the most significant bit first. - Multi-byte writes are also supported, and if ByteOrder = 0, - the bits of !!TXDATA are transmitted strictly in order of - decreasing signficance (i.e. most signicant bit first). - For some processor architectures, this could lead to shuffling - of flash data as compared to how it is written in memory. - In which case, choosing ByteOrder = 1 can reverse the - byte-order of multi-byte data writes. (Though within - each byte the most significant bit is always sent first.) - ''' - swaccess: "wo", - unusual: "false" - } + The serial order of bit transmission + is chosen to match SPI flash devices. Individual bytes + are always transmitted with the most significant bit first. + Multi-byte writes are also supported, and if ByteOrder = 0, + the bits of !!TXDATA are transmitted strictly in order of + decreasing signficance (i.e. most signicant bit first). + For some processor architectures, this could lead to shuffling + of flash data as compared to how it is written in memory. + In which case, choosing ByteOrder = 1 can reverse the + byte-order of multi-byte data writes. (Though within + each byte the most significant bit is always sent first.) + ''' + swaccess: "wo", + hwaccess: "hro", + hwext: "true", + hwqe: "true", + fields: [ + { bits: "31:0", + name: "TXDATA", + desc: "SPI Transmit Data", + resval: "0x0" + } + ] }, + { name: "ERROR_ENABLE", desc: "Controls which classes of errors raise an interrupt." swaccess: "rw", diff --git a/src/spi_host/data/spi_host.json b/src/spi_host/data/spi_host.json new file mode 100644 index 000000000..7defed26c --- /dev/null +++ b/src/spi_host/data/spi_host.json @@ -0,0 +1,500 @@ +{ + "name": "spi_host", + "clocking": [ + { + "clock": "clk_i", + "reset": "rst_ni", + "primary": true + } + ], + "bus_interfaces": [ + { + "protocol": "tlul", + "direction": "device" + } + ], + "inter_signal_list": [ + { + "struct": "passthrough", + "package": "spi_device_pkg", + "type": "req_rsp", + "name": "passthrough", + "act": "rsp", + "width": "1" + } + ], + "regwidth": "32", + "param_list": [ + { + "name": "ByteOrder", + "desc": "Byte order to use when transmitting or receiving data. If ByteOrder = 0,\n the IP uses a Big-Endian ordering for the bytes in DATA.\n The most significant byte (MSB) of DATA is transmitted first, and\n received data is placed in the MSB location of DATA. If ByteOrder = 1,\n a Little-Endian ordering is used for these registers, and the LSB of each\n gets priority for receiving and transmitting data.", + "type": "logic", + "default": "1" + }, + { + "name": "NumCS", + "desc": "The number of active-low chip select (cs_n) lines to create.", + "type": "int", + "default": "2" + }, + { + "name": "TxDepth", + "desc": "The size of the Tx FIFO (in words)", + "type": "int", + "default": "72" + }, + { + "name": "RxDepth", + "desc": "The size of the Rx FIFO (in words)", + "type": "int", + "default": "64" + }, + { + "name": "CmdDepth", + "desc": "The size of the Cmd FIFO (one segment descriptor per entry)", + "type": "int", + "default": "4" + } + ], + "available_output_list": [ + { + "name": "sck", + "desc": "SPI Clock" + }, + { + "name": "csb", + "desc": "Chip Select# (One hot, active low). The size of this port should match NumCS.", + "width": "1" + } + ], + "available_inout_list": [ + { + "name": "sd", + "desc": "SPI data bus", + "width": "4" + } + ], + "interrupt_list": [ + { + "name": "error", + "desc": "Error-related interrupts, see !!ERROR_ENABLE register for more\n information." + }, + { + "name": "spi_event", + "desc": "Event-related interrupts, see !!EVENT_ENABLE register for more\n information." + } + ], + "alert_list": [ + { + "name": "fatal_fault", + "desc": "This fatal alert is triggered when a fatal TL-UL bus integrity fault is detected." + } + ], + "countermeasures": [ + { + "name": "BUS.INTEGRITY", + "desc": "End-to-end bus integrity scheme." + } + ], + "registers": [ + { + "name": "CONTROL", + "desc": "Control register", + "swaccess": "rw", + "hwaccess": "hro", + "fields": [ + { + "bits": "31", + "name": "SPIEN", + "desc": "Enables the SPI host. On reset, this field is 0, meaning\n that no transactions can proceed.", + "resval": "0x0" + }, + { + "bits": "30", + "name": "SW_RST", + "desc": "Clears the entire IP to the reset state when set to 1, including\n the FIFOs, the CDC's, the core state machine and the shift register.\n In the current implementation, the CDC FIFOs are drained not reset.\n Therefore software must confirm that both FIFO's empty before releasing\n the IP from reset.", + "resval": "0x0" + }, + { + "bits": "29", + "name": "OUTPUT_EN", + "desc": "Enable the SPI host output buffers for the sck, csb, and sd lines. This allows\n the SPI_HOST IP to connect to the same bus as other SPI controllers without\n interference.", + "resval": "0x0" + }, + { + "bits": "15:8", + "name": "TX_WATERMARK", + "desc": "If !!EVENT_ENABLE.TXWM is set, the IP will send\n an interrupt when the depth of the TX FIFO drops below\n TX_WATERMARK words (32b each).", + "resval": "0" + }, + { + "bits": "7:0", + "name": "RX_WATERMARK", + "desc": "If !!EVENT_ENABLE.RXWM is set, the IP will send\n an interrupt when the depth of the RX FIFO reaches\n RX_WATERMARK words (32b each).", + "resval": "127" + } + ] + }, + { + "name": "STATUS", + "desc": "Status register", + "swaccess": "ro", + "hwaccess": "hwo", + "fields": [ + { + "bits": "31", + "name": "READY", + "desc": "When high, indicates the SPI host is ready to receive\n commands. Writing to COMMAND when READY is low is\n an error, and will trigger an interrupt.\n ", + "resval": "0x0" + }, + { + "bits": "30", + "name": "ACTIVE", + "desc": "When high, indicates the SPI host is processing a previously\n issued command.", + "resval": "0x0" + }, + { + "bits": "29", + "name": "TXFULL", + "desc": "When high, indicates that the transmit data fifo is full.\n Any further writes to !!RXDATA will create an error interrupt.", + "resval": "0x0" + }, + { + "bits": "28", + "name": "TXEMPTY", + "desc": "When high, indicates that the transmit data fifo is empty.\n ", + "resval": "0x0" + }, + { + "bits": "27", + "name": "TXSTALL", + "desc": "If high, signifies that an ongoing transaction has stalled\n due to lack of data in the TX FIFO", + "resval": "0x0" + }, + { + "bits": "26", + "name": "TXWM", + "desc": "If high, the amount of data in the TX FIFO has fallen below the\n level of !!CONTROL.TX_WATERMARK words (32b each).", + "resval": "0x0" + }, + { + "bits": "25", + "name": "RXFULL", + "desc": "When high, indicates that the receive fifo is full. Any\n ongoing transactions will stall until firmware reads some\n data from !!RXDATA.", + "resval": "0x0" + }, + { + "bits": "24", + "name": "RXEMPTY", + "desc": "When high, indicates that the receive fifo is empty.\n Any reads from RX FIFO will cause an error interrupt.\n ", + "resval": "0x0" + }, + { + "bits": "23", + "name": "RXSTALL", + "desc": "If high, signifies that an ongoing transaction has stalled\n due to lack of available space in the RX FIFO", + "resval": "0x0" + }, + { + "bits": "22", + "name": "BYTEORDER", + "desc": "The value of the ByteOrder parameter, provided so that firmware\n can confirm proper IP configuration." + }, + { + "bits": "20", + "name": "RXWM", + "desc": "If high, the number of 32-bits in the RX FIFO now exceeds the\n !!CONTROL.RX_WATERMARK entries (32b each).", + "resval": "0x0" + }, + { + "bits": "19:16", + "name": "CMDQD", + "desc": "Command queue depth. Indicates how many unread 32-bit words are\n currently in the command segment queue.", + "resval": "0x0" + }, + { + "bits": "15:8", + "name": "RXQD", + "desc": "Receive queue depth. Indicates how many unread 32-bit words are\n currently in the RX FIFO. When active, this result may an\n underestimate due to synchronization delays.", + "resval": "0x0" + }, + { + "bits": "7:0", + "name": "TXQD", + "desc": "Transmit queue depth. Indicates how many unsent 32-bit words\n are currently in the TX FIFO. When active, this result may\n be an overestimate due to synchronization delays,", + "resval": "0x0" + } + ], + "tags": [ + "excl:CsrAllTests:CsrExclCheck" + ] + }, + { + "multireg": { + "name": "CONFIGOPTS", + "desc": "Configuration options register.\n\n Contains options for controlling each peripheral. One register per\n cs_n line", + "swaccess": "rw", + "hwaccess": "hro", + "cname": "configopts", + "count": "NumCS", + "fields": [ + { + "bits": "31", + "name": "CPOL", + "desc": "The polarity of the sck clock signal. When CPOL is 0,\n sck is low when idle, and emits high pulses. When CPOL\n is low, sck is high when idle, and emits a series of low\n pulses.\n ", + "resval": "0x0" + }, + { + "bits": "30", + "name": "CPHA", + "desc": "The phase of the sck clock signal relative to the data. When\n CPHA = 0, the data changes on the trailing edge of sck\n and is typically sampled on the leading edge. Conversely\n if CPHA = 1 high, data lines change on the leading edge of\n sck and are typically sampled on the trailing edge.\n CPHA should be chosen to match the phase of the selected\n device. The sampling behavior is modified by the\n !!CONFIGOPTS.FULLCYC bit.\n ", + "resval": "0x0" + }, + { + "bits": "29", + "name": "FULLCYC", + "desc": "Full cycle. Modifies the CPHA sampling behaviour to allow\n for longer device logic setup times. Rather than sampling the SD\n bus a half cycle after shifting out data, the data is sampled\n a full cycle after shifting data out. This means that if\n CPHA = 0, data is shifted out on the trailing edge, and\n sampled a full cycle later. If CPHA = 1, data is shifted and\n sampled with the trailing edge, also separated by a\n full cycle.", + "resval": 0 + }, + { + "bits": "27:24", + "name": "CSNLEAD", + "desc": "CS_N Leading Time. Indicates the number of half sck cycles,\n CSNLEAD+1, to leave between the falling edge of cs_n and\n the first edge of sck. Setting this register to zero\n corresponds to the minimum delay of one-half sck cycle", + "resval": 0 + }, + { + "bits": "23:20", + "name": "CSNTRAIL", + "desc": "CS_N Trailing Time. Indicates the number of half sck cycles,\n CSNTRAIL+1, to leave between last edge of sck and the rising\n edge of cs_n. Setting this register to zero corresponds\n to the minimum delay of one-half sck cycle.", + "resval": 0 + }, + { + "bits": "19:16", + "name": "CSNIDLE", + "desc": "Minimum idle time between commands. Indicates the minimum\n number of sck half-cycles to hold cs_n high between commands.\n Setting this register to zero creates a minimally-wide CS_N-high\n pulse of one-half sck cycle.", + "resval": 0 + }, + { + "bits": "15:0", + "name": "CLKDIV", + "desc": "Core clock divider. Slows down subsequent SPI transactions by a\n factor of (CLKDIV+1) relative to the core clock frequency. The\n period of sck, T(sck) then becomes `2*(CLK_DIV+1)*T(core)`", + "resval": 0 + } + ] + } + }, + { + "name": "CSID", + "desc": "Chip-Select ID\n\n Controls which device to target with the next command. This register\n is passed to the core whenever !!COMMAND is written. The core then\n asserts cio_csb_o[!!CSID] during the execution of the command.", + "swaccess": "rw", + "hwaccess": "hro", + "fields": [ + { + "bits": "31:0", + "name": "CSID", + "desc": "Chip Select ID", + "resval": "0x0" + } + ] + }, + { + "name": "COMMAND", + "desc": "Command Register\n\n Parameters specific to each command segment. Unlike the !!CONFIGOPTS multi-register,\n there is only one command register for controlling all attached SPI devices", + "swaccess": "wo", + "hwaccess": "hro", + "hwext": "true", + "hwqe": "true", + "fields": [ + { + "bits": "13:12", + "name": "DIRECTION", + "desc": "The direction for the following command: \"0\" = Dummy cycles\n (no TX/RX). \"1\" = Rx only, \"2\" = Tx only, \"3\" = Bidirectional\n Tx/Rx (Standard SPI mode only).", + "resval": "0x0" + }, + { + "bits": "11:10", + "name": "SPEED", + "desc": "The speed for this command segment: \"0\" = Standard SPI. \"1\" = Dual SPI.\n \"2\"=Quad SPI, \"3\": RESERVED.", + "resval": "0x0" + }, + { + "bits": "9", + "name": "CSAAT", + "desc": "Chip select active after transaction. If CSAAT = 0, the\n chip select line is raised immediately at the end of the\n command segment. If !!COMMAND.CSAAT = 1, the chip select\n line is left low at the end of the current transaction\n segment. This allows the creation longer, more\n complete SPI transactions, consisting of several separate\n segments for issuing instructions, pausing for dummy cycles,\n and transmitting or receiving data from the device.", + "resval": "0x0" + }, + { + "bits": "8:0", + "name": "LEN", + "desc": "Segment Length.\n\n For read or write segments, this field controls the\n number of 1-byte bursts to transmit and or receive in\n this command segment. The number of cyles required\n to send or received a byte will depend on !!COMMAND.SPEED.\n For dummy segments, (!!COMMAND.DIRECTION == 0), this register\n controls the number of dummy cycles to issue.\n The number of bytes (or dummy cycles) in the segment will be\n equal to !!COMMAND.LEN + 1.\n ", + "resval": "0x0" + } + ], + "tags": [ + "excl:CsrAllTests:CsrExclWrite" + ] + }, + { + "name": "RXDATA", + "desc": "SPI Receive Data\n\n Reads from this window pull data from the RXFIFO.\n\n The serial order of bit transmission\n is chosen to match SPI flash devices. Individual bytes\n are always transmitted with the most significant bit first.\n Only four-bute reads are supported. If ByteOrder = 0,\n the first byte received is packed in the MSB of !!RXDATA.\n For some processor architectures, this could lead to shuffling\n of flash data as compared to how it is written in memory.\n In which case, choosing ByteOrder = 1 can reverse the\n byte-order of each data read, causing the first byte\n received to be packed into the LSB of !!RXDATA. (Though within\n each byte the most significant bit is always pulled\n from the bus first.)\n ", + "swaccess": "ro", + "hwaccess": "hwo", + "fields": [ + { + "bits": "31:0", + "name": "RXDATA", + "desc": "SPI Receive Data", + "resval": "0x0" + } + ] + }, + { + "name": "TXDATA", + "desc": "SPI Transmit Data\n Data written to this window is placed into the TXFIFO.\n Byte-enables are supported for writes.\n\n The serial order of bit transmission\n is chosen to match SPI flash devices. Individual bytes\n are always transmitted with the most significant bit first.\n Multi-byte writes are also supported, and if ByteOrder = 0,\n the bits of !!TXDATA are transmitted strictly in order of\n decreasing signficance (i.e. most signicant bit first).\n For some processor architectures, this could lead to shuffling\n of flash data as compared to how it is written in memory.\n In which case, choosing ByteOrder = 1 can reverse the\n byte-order of multi-byte data writes. (Though within\n each byte the most significant bit is always sent first.)\n ", + "swaccess": "wo", + "hwaccess": "hro", + "hwext": "true", + "hwqe": "true", + "fields": [ + { + "bits": "31:0", + "name": "TXDATA", + "desc": "SPI Transmit Data", + "resval": "0x0" + } + ] + }, + { + "name": "ERROR_ENABLE", + "desc": "Controls which classes of errors raise an interrupt.", + "swaccess": "rw", + "hwaccess": "hro", + "fields": [ + { + "bits": "4", + "name": "CSIDINVAL", + "desc": "Invalid CSID: If this bit is set, the block sends an error interrupt whenever\n a command is submitted, but CSID exceeds NumCS.", + "resval": "0x1" + }, + { + "bits": "3", + "name": "CMDINVAL", + "desc": "Invalid Command Errors: If this bit is set, the block sends an\n error interrupt whenever a command is sent with invalid values for\n !!COMMAND.SPEED or !!COMMAND.DIRECTION.", + "resval": "0x1" + }, + { + "bits": "2", + "name": "UNDERFLOW", + "desc": "Underflow Errors: If this bit is set, the block sends an\n error interrupt whenever there is a read from !!RXDATA\n but the RX FIFO is empty.", + "resval": "0x1" + }, + { + "bits": "1", + "name": "OVERFLOW", + "desc": "Overflow Errors: If this bit is set, the block sends an\n error interrupt whenever the TX FIFO overflows.", + "resval": "0x1" + }, + { + "bits": "0", + "name": "CMDBUSY", + "desc": "Command Error: If this bit is set, the block sends an error\n interrupt whenever a command is issued while busy (i.e. a 1 is\n when !!STATUS.READY is not asserted.)", + "resval": "0x1" + } + ] + }, + { + "name": "ERROR_STATUS", + "desc": "Indicates that any errors that have occurred.\n When an error\n occurs, the corresponding bit must be cleared here before\n issuing any further commands.", + "swaccess": "rw1c", + "hwaccess": "hrw", + "fields": [ + { + "bits": "5", + "name": "ACCESSINVAL", + "desc": "Indicates that TLUL attempted to write to TXDATA with no bytes enabled. Such\n 'zero byte' writes are not supported.", + "resval": "0x0", + "tags": [ + "excl:CsrAllTests:CsrExclCheck" + ] + }, + { + "bits": "4", + "name": "CSIDINVAL", + "desc": "Indicates a command was attempted with an invalid value for !!CSID.", + "resval": "0x0" + }, + { + "bits": "3", + "name": "CMDINVAL", + "desc": "Indicates an invalid command segment, meaning either an invalid value of\n !!COMMAND.SPEED or a request for bidirectional data transfer at dual or quad\n speed", + "resval": "0x0" + }, + { + "bits": "2", + "name": "UNDERFLOW", + "desc": "Indicates that firmware has attempted to read from\n !!RXDATA when the RX FIFO is empty.", + "resval": "0x0" + }, + { + "bits": "1", + "name": "OVERFLOW", + "desc": "Indicates that firmware has overflowed the TX FIFO", + "resval": "0x0", + "tags": [ + "excl:CsrAllTests:CsrExclCheck" + ] + }, + { + "bits": "0", + "name": "CMDBUSY", + "desc": "Indicates a write to !!COMMAND when !!STATUS.READY = 0.\n ", + "resval": "0x0" + } + ] + }, + { + "name": "EVENT_ENABLE", + "desc": "Controls which classes of SPI events raise an interrupt.", + "swaccess": "rw", + "hwaccess": "hro", + "fields": [ + { + "bits": "5", + "name": "IDLE", + "desc": "Assert to send a spi_event interrupt whenever !!STATUS.ACTIVE\n goes low", + "resval": "0x0" + }, + { + "bits": "4", + "name": "READY", + "desc": "Assert to send a spi_event interrupt whenever !!STATUS.READY\n goes high", + "resval": "0x0" + }, + { + "bits": "3", + "name": "TXWM", + "desc": "Assert to send a spi_event interrupt whenever the number of 32-bit words in\n the TX FIFO is less than !!CONTROL.TX_WATERMARK. To prevent the\n reassertion of this interrupt add more data to the TX FIFO, or\n reduce !!CONTROL.TX_WATERMARK.", + "resval": "0x0" + }, + { + "bits": "2", + "name": "RXWM", + "desc": "Assert to send a spi_event interrupt whenever the number of 32-bit words in\n the RX FIFO is greater than !!CONTROL.RX_WATERMARK. To prevent the\n reassertion of this interrupt, read more data from the RX FIFO, or\n increase !!CONTROL.RX_WATERMARK.", + "resval": "0x0" + }, + { + "bits": "1", + "name": "TXEMPTY", + "desc": "Assert to send a spi_event interrupt whenever !!STATUS.TXEMPTY\n goes high", + "resval": "0x0" + }, + { + "bits": "0", + "name": "RXFULL", + "desc": "Assert to send a spi_event interrupt whenever !!STATUS.RXFULL\n goes high", + "resval": "0x0" + } + ] + } + ] +} diff --git a/src/spi_host/data/spi_host.rdl b/src/spi_host/data/spi_host.rdl new file mode 100644 index 000000000..20e1cb6b6 --- /dev/null +++ b/src/spi_host/data/spi_host.rdl @@ -0,0 +1,367 @@ +addrmap spi_host { + reg { + field { + sw = rw; + onwrite = woclr; + desc = "Error-related interrupts, see !!ERROR_ENABLE register for more + information."; + } ERROR[0:0]; + field { + sw = rw; + onwrite = woclr; + desc = "Event-related interrupts, see !!EVENT_ENABLE register for more + information."; + } SPI_EVENT[1:1]; + } INTERRUPT_STATE @ 0x0; + reg { + field { + sw = rw; + onwrite = woclr; + desc = "Enable interrupt when error is set."; + } ERROR[0:0]; + field { + sw = rw; + onwrite = woclr; + desc = "Enable interrupt when spi_event is set."; + } SPI_EVENT[1:1]; + } INTERRUPT_ENABLE @ 0x4; + reg { + field { + sw = w; + desc = "Write 1 to force error to 1."; + } ERROR[0:0]; + field { + sw = w; + desc = "Write 1 to force spi_event to 1."; + } SPI_EVENT[1:1]; + } INTERRUPT_TEST @ 0x8; + reg { + field { + sw = w; + desc = "Write 1 to trigger one alert event of this kind."; + } FATAL_FAULT[0:0]; + } ALERT_TEST @ 0xC; + reg { + field { + desc = "If !!EVENT_ENABLE.RXWM is set, the IP will send + an interrupt when the depth of the RX FIFO reaches + RX_WATERMARK words (32b each)."; + sw = rw; + } RX_WATERMARK[7:0] = 0x7F; + field { + desc = "If !!EVENT_ENABLE.TXWM is set, the IP will send + an interrupt when the depth of the TX FIFO drops below + TX_WATERMARK words (32b each)."; + sw = rw; + } TX_WATERMARK[15:8] = 0x0; + field { + desc = "Enable the SPI host output buffers for the sck, csb, and sd lines. This allows + the SPI_HOST IP to connect to the same bus as other SPI controllers without + interference."; + sw = rw; + } OUTPUT_EN[29:29] = 0x0; + field { + desc = "Clears the entire IP to the reset state when set to 1, including + the FIFOs, the CDC's, the core state machine and the shift register. + In the current implementation, the CDC FIFOs are drained not reset. + Therefore software must confirm that both FIFO's empty before releasing + the IP from reset."; + sw = rw; + } SW_RST[30:30] = 0x0; + field { + desc = "Enables the SPI host. On reset, this field is 0, meaning + that no transactions can proceed."; + sw = rw; + } SPIEN[31:31] = 0x0; + } CONTROL @ 0x10; + reg { + field { + desc = "Transmit queue depth. Indicates how many unsent 32-bit words + are currently in the TX FIFO. When active, this result may + be an overestimate due to synchronization delays,"; + sw = r; + } TXQD[7:0] = 0x0; + field { + desc = "Receive queue depth. Indicates how many unread 32-bit words are + currently in the RX FIFO. When active, this result may an + underestimate due to synchronization delays."; + sw = r; + } RXQD[15:8] = 0x0; + field { + desc = "Command queue depth. Indicates how many unread 32-bit words are + currently in the command segment queue."; + sw = r; + } CMDQD[19:16] = 0x0; + field { + desc = "If high, the number of 32-bits in the RX FIFO now exceeds the + !!CONTROL.RX_WATERMARK entries (32b each)."; + sw = r; + } RXWM[20:20] = 0x0; + field { + desc = "The value of the ByteOrder parameter, provided so that firmware + can confirm proper IP configuration."; + sw = r; + } BYTEORDER[22:22]; + field { + desc = "If high, signifies that an ongoing transaction has stalled + due to lack of available space in the RX FIFO"; + sw = r; + } RXSTALL[23:23] = 0x0; + field { + desc = "When high, indicates that the receive fifo is empty. + Any reads from RX FIFO will cause an error interrupt."; + sw = r; + } RXEMPTY[24:24] = 0x0; + field { + desc = "When high, indicates that the receive fifo is full. Any + ongoing transactions will stall until firmware reads some + data from !!RXDATA."; + sw = r; + } RXFULL[25:25] = 0x0; + field { + desc = "If high, the amount of data in the TX FIFO has fallen below the + level of !!CONTROL.TX_WATERMARK words (32b each)."; + sw = r; + } TXWM[26:26] = 0x0; + field { + desc = "If high, signifies that an ongoing transaction has stalled + due to lack of data in the TX FIFO"; + sw = r; + } TXSTALL[27:27] = 0x0; + field { + desc = "When high, indicates that the transmit data fifo is empty."; + sw = r; + } TXEMPTY[28:28] = 0x0; + field { + desc = "When high, indicates that the transmit data fifo is full. + Any further writes to !!RXDATA will create an error interrupt."; + sw = r; + } TXFULL[29:29] = 0x0; + field { + desc = "When high, indicates the SPI host is processing a previously + issued command."; + sw = r; + } ACTIVE[30:30] = 0x0; + field { + desc = "When high, indicates the SPI host is ready to receive + commands. Writing to COMMAND when READY is low is + an error, and will trigger an interrupt."; + sw = r; + } READY[31:31] = 0x0; + } STATUS @ 0x14; + reg { + field { + desc = "Core clock divider. Slows down subsequent SPI transactions by a + factor of (CLKDIV+1) relative to the core clock frequency. The + period of sck, T(sck) then becomes `2*(CLK_DIV+1)*T(core)`"; + sw = rw; + } CLKDIV[15:0] = 0x0; + field { + desc = "Minimum idle time between commands. Indicates the minimum + number of sck half-cycles to hold cs_n high between commands. + Setting this register to zero creates a minimally-wide CS_N-high + pulse of one-half sck cycle."; + sw = rw; + } CSNIDLE[19:16] = 0x0; + field { + desc = "CS_N Trailing Time. Indicates the number of half sck cycles, + CSNTRAIL+1, to leave between last edge of sck and the rising + edge of cs_n. Setting this register to zero corresponds + to the minimum delay of one-half sck cycle."; + sw = rw; + } CSNTRAIL[23:20] = 0x0; + field { + desc = "CS_N Leading Time. Indicates the number of half sck cycles, + CSNLEAD+1, to leave between the falling edge of cs_n and + the first edge of sck. Setting this register to zero + corresponds to the minimum delay of one-half sck cycle"; + sw = rw; + } CSNLEAD[27:24] = 0x0; + field { + desc = "Full cycle. Modifies the CPHA sampling behaviour to allow + for longer device logic setup times. Rather than sampling the SD + bus a half cycle after shifting out data, the data is sampled + a full cycle after shifting data out. This means that if + CPHA = 0, data is shifted out on the trailing edge, and + sampled a full cycle later. If CPHA = 1, data is shifted and + sampled with the trailing edge, also separated by a + full cycle."; + sw = rw; + } FULLCYC[29:29] = 0x0; + field { + desc = "The phase of the sck clock signal relative to the data. When + CPHA = 0, the data changes on the trailing edge of sck + and is typically sampled on the leading edge. Conversely + if CPHA = 1 high, data lines change on the leading edge of + sck and are typically sampled on the trailing edge. + CPHA should be chosen to match the phase of the selected + device. The sampling behavior is modified by the + !!CONFIGOPTS.FULLCYC bit."; + sw = rw; + } CPHA[30:30] = 0x0; + field { + desc = "The polarity of the sck clock signal. When CPOL is 0, + sck is low when idle, and emits high pulses. When CPOL + is low, sck is high when idle, and emits a series of low + pulses."; + sw = rw; + } CPOL[31:31] = 0x0; + } CONFIGOPTS[2] @ 0x18 += 0x4; + reg { + field { + desc = "Chip Select ID"; + sw = rw; + } CSID[31:0] = 0x0; + } CSID @ 0x20; + reg { + field { + desc = "Segment Length. + + For read or write segments, this field controls the + number of 1-byte bursts to transmit and or receive in + this command segment. The number of cyles required + to send or received a byte will depend on !!COMMAND.SPEED. + For dummy segments, (!!COMMAND.DIRECTION == 0), this register + controls the number of dummy cycles to issue. + The number of bytes (or dummy cycles) in the segment will be + equal to !!COMMAND.LEN + 1."; + sw = w; + } LEN[8:0] = 0x0; + field { + desc = "Chip select active after transaction. If CSAAT = 0, the + chip select line is raised immediately at the end of the + command segment. If !!COMMAND.CSAAT = 1, the chip select + line is left low at the end of the current transaction + segment. This allows the creation longer, more + complete SPI transactions, consisting of several separate + segments for issuing instructions, pausing for dummy cycles, + and transmitting or receiving data from the device."; + sw = w; + } CSAAT[9:9] = 0x0; + field { + desc = "The speed for this command segment: \"0\" = Standard SPI. \"1\" = Dual SPI. + \"2\"=Quad SPI, \"3\": RESERVED."; + sw = w; + } SPEED[11:10] = 0x0; + field { + desc = "The direction for the following command: \"0\" = Dummy cycles + (no TX/RX). \"1\" = Rx only, \"2\" = Tx only, \"3\" = Bidirectional + Tx/Rx (Standard SPI mode only)."; + sw = w; + } DIRECTION[13:12] = 0x0; + } COMMAND @ 0x24; + reg { + field { + desc = "SPI Receive Data"; + sw = r; + } RXDATA[31:0] = 0x0; + } RXDATA @ 0x28; + reg { + field { + desc = "SPI Transmit Data"; + sw = w; + } TXDATA[31:0] = 0x0; + } TXDATA @ 0x2C; + reg { + field { + desc = "Command Error: If this bit is set, the block sends an error + interrupt whenever a command is issued while busy (i.e. a 1 is + when !!STATUS.READY is not asserted.)"; + sw = rw; + } CMDBUSY[0:0] = 0x1; + field { + desc = "Overflow Errors: If this bit is set, the block sends an + error interrupt whenever the TX FIFO overflows."; + sw = rw; + } OVERFLOW[1:1] = 0x1; + field { + desc = "Underflow Errors: If this bit is set, the block sends an + error interrupt whenever there is a read from !!RXDATA + but the RX FIFO is empty."; + sw = rw; + } UNDERFLOW[2:2] = 0x1; + field { + desc = "Invalid Command Errors: If this bit is set, the block sends an + error interrupt whenever a command is sent with invalid values for + !!COMMAND.SPEED or !!COMMAND.DIRECTION."; + sw = rw; + } CMDINVAL[3:3] = 0x1; + field { + desc = "Invalid CSID: If this bit is set, the block sends an error interrupt whenever + a command is submitted, but CSID exceeds NumCS."; + sw = rw; + } CSIDINVAL[4:4] = 0x1; + } ERROR_ENABLE @ 0x30; + reg { + field { + desc = "Indicates a write to !!COMMAND when !!STATUS.READY = 0."; + sw = rw; + onwrite = woclr; + } CMDBUSY[0:0] = 0x0; + field { + desc = "Indicates that firmware has overflowed the TX FIFO"; + sw = rw; + onwrite = woclr; + } OVERFLOW[1:1] = 0x0; + field { + desc = "Indicates that firmware has attempted to read from + !!RXDATA when the RX FIFO is empty."; + sw = rw; + onwrite = woclr; + } UNDERFLOW[2:2] = 0x0; + field { + desc = "Indicates an invalid command segment, meaning either an invalid value of + !!COMMAND.SPEED or a request for bidirectional data transfer at dual or quad + speed"; + sw = rw; + onwrite = woclr; + } CMDINVAL[3:3] = 0x0; + field { + desc = "Indicates a command was attempted with an invalid value for !!CSID."; + sw = rw; + onwrite = woclr; + } CSIDINVAL[4:4] = 0x0; + field { + desc = "Indicates that TLUL attempted to write to TXDATA with no bytes enabled. Such + 'zero byte' writes are not supported."; + sw = rw; + onwrite = woclr; + } ACCESSINVAL[5:5] = 0x0; + } ERROR_STATUS @ 0x34; + reg { + field { + desc = "Assert to send a spi_event interrupt whenever !!STATUS.RXFULL + goes high"; + sw = rw; + } RXFULL[0:0] = 0x0; + field { + desc = "Assert to send a spi_event interrupt whenever !!STATUS.TXEMPTY + goes high"; + sw = rw; + } TXEMPTY[1:1] = 0x0; + field { + desc = "Assert to send a spi_event interrupt whenever the number of 32-bit words in + the RX FIFO is greater than !!CONTROL.RX_WATERMARK. To prevent the + reassertion of this interrupt, read more data from the RX FIFO, or + increase !!CONTROL.RX_WATERMARK."; + sw = rw; + } RXWM[2:2] = 0x0; + field { + desc = "Assert to send a spi_event interrupt whenever the number of 32-bit words in + the TX FIFO is less than !!CONTROL.TX_WATERMARK. To prevent the + reassertion of this interrupt add more data to the TX FIFO, or + reduce !!CONTROL.TX_WATERMARK."; + sw = rw; + } TXWM[3:3] = 0x0; + field { + desc = "Assert to send a spi_event interrupt whenever !!STATUS.READY + goes high"; + sw = rw; + } READY[4:4] = 0x0; + field { + desc = "Assert to send a spi_event interrupt whenever !!STATUS.ACTIVE + goes low"; + sw = rw; + } IDLE[5:5] = 0x0; + } EVENT_ENABLE @ 0x38; +}; \ No newline at end of file diff --git a/src/spi_host/rtl/spi_host.sv b/src/spi_host/rtl/spi_host.sv index 6ef35668c..6fc6fc796 100644 --- a/src/spi_host/rtl/spi_host.sv +++ b/src/spi_host/rtl/spi_host.sv @@ -11,14 +11,25 @@ module spi_host import spi_host_reg_pkg::*; #( - parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}} + parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + parameter AHBDataWidth = 64, + parameter AHBAddrWidth = 32 ) ( input clk_i, input rst_ni, - // Register interface - input tlul_pkg::tl_h2d_t tl_i, - output tlul_pkg::tl_d2h_t tl_o, + // AMBA AHB Lite Interface + input logic [AHBAddrWidth-1:0] haddr_i, + input logic [AHBDataWidth-1:0] hwdata_i, + input logic hsel_i, + input logic hwrite_i, + input logic hready_i, + input logic [1:0] htrans_i, + input logic [2:0] hsize_i, + + output logic hresp_o, + output logic hreadyout_o, + output logic [AHBDataWidth-1:0] hrdata_o, // Alerts input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, @@ -33,10 +44,6 @@ module spi_host output logic [3:0] cio_sd_en_o, input [3:0] cio_sd_i, - // Passthrough interface - input spi_device_pkg::passthrough_req_t passthrough_i, - output spi_device_pkg::passthrough_rsp_t passthrough_o, - output logic intr_error_o, output logic intr_spi_event_o ); @@ -46,18 +53,27 @@ module spi_host spi_host_reg2hw_t reg2hw; spi_host_hw2reg_t hw2reg; - tlul_pkg::tl_h2d_t fifo_win_h2d [2]; - tlul_pkg::tl_d2h_t fifo_win_d2h [2]; + logic fifo_rx_re; // Register module logic [NumAlerts-1:0] alert_test, alerts; - spi_host_reg_top u_reg ( + spi_host_reg_top #( + .AHBDataWidth(AHBDataWidth), + .AHBAddrWidth(AHBAddrWidth) + ) u_reg ( .clk_i, .rst_ni, - .tl_i (tl_i), - .tl_o (tl_o), - .tl_win_o (fifo_win_h2d), - .tl_win_i (fifo_win_d2h), + .haddr_i, + .hwdata_i, + .hsel_i, + .hwrite_i, + .hready_i, + .htrans_i, + .hsize_i, + .hresp_o, + .hreadyout_o, + .hrdata_o, + .fifo_rx_re, .reg2hw, .hw2reg, // SEC_CM: BUS.INTEGRITY @@ -78,12 +94,12 @@ module spi_host ) u_prim_alert_sender ( .clk_i, .rst_ni, - .alert_test_i ( alert_test[i] ), - .alert_req_i ( alerts[0] ), - .alert_ack_o ( ), - .alert_state_o ( ), - .alert_rx_i ( alert_rx_i[i] ), - .alert_tx_o ( alert_tx_o[i] ) + .alert_test_i (alert_test[i] ), + .alert_req_i (alerts[0] ), + .alert_ack_o (), + .alert_state_o (), + .alert_rx_i (alert_rx_i[i] ), + .alert_tx_o (alert_tx_o[i] ) ); end @@ -98,62 +114,13 @@ module spi_host assign sd_en = output_en ? sd_en_core : 4'h0; - if (NumCS == 1) begin : gen_passthrough_implementation - logic passthrough_en; - assign passthrough_en = passthrough_i.passthrough_en; - - logic pt_sck; - logic pt_sck_en; - logic [0:0] pt_csb; - logic [0:0] pt_csb_en; - logic [3:0] pt_sd_out; - logic [3:0] pt_sd_en; - - assign pt_sck = passthrough_i.sck; - assign pt_sck_en = passthrough_i.sck_en; - assign pt_csb[0] = passthrough_i.csb; - assign pt_csb_en[0] = passthrough_i.csb_en; - assign pt_sd_out = passthrough_i.s; - assign pt_sd_en = passthrough_i.s_en; - - assign cio_sck_o = passthrough_en ? pt_sck : sck; - assign cio_sck_en_o = passthrough_en ? pt_sck_en : output_en; - assign cio_csb_o = passthrough_en ? pt_csb : csb; - assign cio_csb_en_o = passthrough_en ? pt_csb_en : output_en; - assign cio_sd_o = passthrough_en ? pt_sd_out : sd_out; - assign cio_sd_en_o = passthrough_en ? pt_sd_en : sd_en; - - end : gen_passthrough_implementation - else begin : gen_passthrough_ignore - // Passthrough only supported for instances with one CSb line - `ASSERT(PassthroughNumCSCompat_A, !passthrough_i.passthrough_en, clk_i, rst_ni) - - assign cio_sck_o = sck; - assign cio_sck_en_o = output_en; - assign cio_csb_o = csb; - assign cio_csb_en_o = {NumCS{output_en}}; - assign cio_sd_o = sd_out; - assign cio_sd_en_o = sd_en; - - logic unused_pt_en; - logic unused_pt_sck; - logic unused_pt_sck_en; - logic unused_pt_csb; - logic unused_pt_csb_en; - logic [3:0] unused_pt_sd_out; - logic [3:0] unused_pt_sd_en; - - assign unused_pt_en = passthrough_i.passthrough_en; - assign unused_pt_sck = passthrough_i.sck; - assign unused_pt_sck_en = passthrough_i.sck_en; - assign unused_pt_csb = passthrough_i.csb; - assign unused_pt_csb_en = passthrough_i.csb_en; - assign unused_pt_sd_out = passthrough_i.s; - assign unused_pt_sd_en = passthrough_i.s_en; - - end : gen_passthrough_ignore - - assign passthrough_o.s = cio_sd_i; + assign cio_sck_o = sck; + assign cio_sck_en_o = output_en; + assign cio_csb_o = csb; + assign cio_csb_en_o = {NumCS{output_en}}; + assign cio_sd_o = sd_out; + assign cio_sd_en_o = sd_en; + assign sd_i = cio_sd_i; assign hw2reg.status.byteorder.d = ByteOrder; @@ -295,20 +262,6 @@ module spi_host logic rx_valid; logic rx_ready; - spi_host_window u_window ( - .clk_i, - .rst_ni, - .rx_win_i (fifo_win_h2d[0]), - .rx_win_o (fifo_win_d2h[0]), - .tx_win_i (fifo_win_h2d[1]), - .tx_win_o (fifo_win_d2h[1]), - .tx_data_o (tx_data), - .tx_be_o (tx_be), - .tx_valid_o (tx_valid), - .rx_data_i (rx_data), - .rx_ready_o (rx_ready) - ); - logic [31:0] core_tx_data; logic [3:0] core_tx_be; logic core_tx_valid; @@ -326,6 +279,14 @@ module spi_host logic tx_empty, tx_full, tx_wm; logic rx_empty, rx_full, rx_wm; + assign tx_data = reg2hw.txdata.q; + assign tx_be = '1; // all ones + assign tx_valid = reg2hw.txdata.qe; + + assign hw2reg.rxdata.d = rx_data; + assign hw2reg.rxdata.de = rx_valid; + assign rx_ready = fifo_rx_re; + assign rx_watermark = reg2hw.control.rx_watermark.q; assign tx_watermark = reg2hw.control.tx_watermark.q; @@ -600,26 +561,19 @@ module spi_host .intr_o (intr_spi_event_o) ); + // Outputs should have a known value after reset + `CALIPTRA_ASSERT_KNOWN(AHBRespKnownO_A, hresp_o) + `CALIPTRA_ASSERT_KNOWN(AHBReadyKnownO_A, hreadyout_o) - `ASSERT_KNOWN(TlDValidKnownO_A, tl_o.d_valid) - `ASSERT_KNOWN(TlAReadyKnownO_A, tl_o.a_ready) - `ASSERT_KNOWN(AlertKnownO_A, alert_tx_o) - `ASSERT_KNOWN(CioSckKnownO_A, cio_sck_o) - `ASSERT_KNOWN(CioSckEnKnownO_A, cio_sck_en_o) - `ASSERT_KNOWN(CioCsbKnownO_A, cio_csb_o) - `ASSERT_KNOWN(CioCsbEnKnownO_A, cio_csb_en_o) - `ASSERT_KNOWN_IF(CioSdKnownO_A, cio_sd_o, !passthrough_i.passthrough_en | - (passthrough_i.passthrough_en && passthrough_i.csb_en && !passthrough_i.csb), - passthrough_i.sck_en & passthrough_i.sck) - `ASSERT_KNOWN(CioSdEnKnownO_A, cio_sd_en_o) - `ASSERT_KNOWN(IntrSpiEventKnownO_A, intr_spi_event_o) - `ASSERT_KNOWN(IntrErrorKnownO_A, intr_error_o) - - // passthrough_o.s is passed through to spi_device, it may contain unknown data, - // but the unknown data won't be used based on the SPI protocol. - // Hence, instead of checking known data, here does a connectivity check. - `ASSERT(PassthroughConn_A, passthrough_o.s === cio_sd_i) + `CALIPTRA_ASSERT_KNOWN(AlertKnownO_A, alert_tx_o) + `CALIPTRA_ASSERT_KNOWN(CioSckKnownO_A, cio_sck_o) + `CALIPTRA_ASSERT_KNOWN(CioSckEnKnownO_A, cio_sck_en_o) + `CALIPTRA_ASSERT_KNOWN(CioCsbKnownO_A, cio_csb_o) + `CALIPTRA_ASSERT_KNOWN(CioCsbEnKnownO_A, cio_csb_en_o) + `CALIPTRA_ASSERT_KNOWN(CioSdEnKnownO_A, cio_sd_en_o) + `CALIPTRA_ASSERT_KNOWN(IntrSpiEventKnownO_A, intr_spi_event_o) + `CALIPTRA_ASSERT_KNOWN(IntrErrorKnownO_A, intr_error_o) // Alert assertions for reg_we onehot check - `ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[0]) + `CALIPTRA_ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[0]) endmodule : spi_host diff --git a/src/spi_host/rtl/spi_host_fsm.sv b/src/spi_host/rtl/spi_host_fsm.sv index 7ab175473..4f7996042 100644 --- a/src/spi_host/rtl/spi_host_fsm.sv +++ b/src/spi_host/rtl/spi_host_fsm.sv @@ -587,10 +587,10 @@ module spi_host_fsm // Assertions confirming valid user input. // - `ASSERT(BidirOnlyInStdMode_A, + `CALIPTRA_ASSERT(BidirOnlyInStdMode_A, cmd_speed_d == Standard || !(cmd_rd_en_d && cmd_wr_en_d), clk_i, rst_ni) - `ASSERT(ValidSpeed_A, cmd_speed_d != RsvdSpd, clk_i, rst_ni) - `ASSERT(ValidCSID_A, csid < NumCS, clk_i, rst_ni) + `CALIPTRA_ASSERT(ValidSpeed_A, cmd_speed_d != RsvdSpd, clk_i, rst_ni) + `CALIPTRA_ASSERT(ValidCSID_A, csid < NumCS, clk_i, rst_ni) endmodule diff --git a/src/spi_host/rtl/spi_host_reg_pkg.sv b/src/spi_host/rtl/spi_host_reg_pkg.sv index 587f8ead2..3a5a9ba09 100644 --- a/src/spi_host/rtl/spi_host_reg_pkg.sv +++ b/src/spi_host/rtl/spi_host_reg_pkg.sv @@ -8,7 +8,7 @@ package spi_host_reg_pkg; // Param list parameter logic ByteOrder = 1; - parameter int NumCS = 1; + parameter int NumCS = 2; parameter int TxDepth = 72; parameter int RxDepth = 64; parameter int CmdDepth = 4; @@ -120,6 +120,11 @@ package spi_host_reg_pkg; } direction; } spi_host_reg2hw_command_reg_t; + typedef struct packed { + logic [31:0] q; + logic qe; + } spi_host_reg2hw_txdata_reg_t; + typedef struct packed { struct packed { logic q; @@ -250,6 +255,11 @@ package spi_host_reg_pkg; } ready; } spi_host_hw2reg_status_reg_t; + typedef struct packed { + logic [31:0] d; + logic de; + } spi_host_hw2reg_rxdata_reg_t; + typedef struct packed { struct packed { logic d; @@ -279,14 +289,15 @@ package spi_host_reg_pkg; // Register -> HW type typedef struct packed { - spi_host_reg2hw_intr_state_reg_t intr_state; // [126:125] - spi_host_reg2hw_intr_enable_reg_t intr_enable; // [124:123] - spi_host_reg2hw_intr_test_reg_t intr_test; // [122:119] - spi_host_reg2hw_alert_test_reg_t alert_test; // [118:117] - spi_host_reg2hw_control_reg_t control; // [116:98] - spi_host_reg2hw_configopts_mreg_t [0:0] configopts; // [97:67] - spi_host_reg2hw_csid_reg_t csid; // [66:35] - spi_host_reg2hw_command_reg_t command; // [34:17] + spi_host_reg2hw_intr_state_reg_t intr_state; // [190:189] + spi_host_reg2hw_intr_enable_reg_t intr_enable; // [188:187] + spi_host_reg2hw_intr_test_reg_t intr_test; // [186:183] + spi_host_reg2hw_alert_test_reg_t alert_test; // [182:181] + spi_host_reg2hw_control_reg_t control; // [180:162] + spi_host_reg2hw_configopts_mreg_t [1:0] configopts; // [161:100] + spi_host_reg2hw_csid_reg_t csid; // [99:68] + spi_host_reg2hw_command_reg_t command; // [67:50] + spi_host_reg2hw_txdata_reg_t txdata; // [49:17] spi_host_reg2hw_error_enable_reg_t error_enable; // [16:12] spi_host_reg2hw_error_status_reg_t error_status; // [11:6] spi_host_reg2hw_event_enable_reg_t event_enable; // [5:0] @@ -294,8 +305,9 @@ package spi_host_reg_pkg; // HW -> register type typedef struct packed { - spi_host_hw2reg_intr_state_reg_t intr_state; // [60:57] - spi_host_hw2reg_status_reg_t status; // [56:12] + spi_host_hw2reg_intr_state_reg_t intr_state; // [92:89] + spi_host_hw2reg_status_reg_t status; // [88:44] + spi_host_hw2reg_rxdata_reg_t rxdata; // [43:12] spi_host_hw2reg_error_status_reg_t error_status; // [11:0] } spi_host_hw2reg_t; @@ -306,12 +318,15 @@ package spi_host_reg_pkg; parameter logic [BlockAw-1:0] SPI_HOST_ALERT_TEST_OFFSET = 6'h c; parameter logic [BlockAw-1:0] SPI_HOST_CONTROL_OFFSET = 6'h 10; parameter logic [BlockAw-1:0] SPI_HOST_STATUS_OFFSET = 6'h 14; - parameter logic [BlockAw-1:0] SPI_HOST_CONFIGOPTS_OFFSET = 6'h 18; - parameter logic [BlockAw-1:0] SPI_HOST_CSID_OFFSET = 6'h 1c; - parameter logic [BlockAw-1:0] SPI_HOST_COMMAND_OFFSET = 6'h 20; - parameter logic [BlockAw-1:0] SPI_HOST_ERROR_ENABLE_OFFSET = 6'h 2c; - parameter logic [BlockAw-1:0] SPI_HOST_ERROR_STATUS_OFFSET = 6'h 30; - parameter logic [BlockAw-1:0] SPI_HOST_EVENT_ENABLE_OFFSET = 6'h 34; + parameter logic [BlockAw-1:0] SPI_HOST_CONFIGOPTS_0_OFFSET = 6'h 18; + parameter logic [BlockAw-1:0] SPI_HOST_CONFIGOPTS_1_OFFSET = 6'h 1c; + parameter logic [BlockAw-1:0] SPI_HOST_CSID_OFFSET = 6'h 20; + parameter logic [BlockAw-1:0] SPI_HOST_COMMAND_OFFSET = 6'h 24; + parameter logic [BlockAw-1:0] SPI_HOST_RXDATA_OFFSET = 6'h 28; + parameter logic [BlockAw-1:0] SPI_HOST_TXDATA_OFFSET = 6'h 2c; + parameter logic [BlockAw-1:0] SPI_HOST_ERROR_ENABLE_OFFSET = 6'h 30; + parameter logic [BlockAw-1:0] SPI_HOST_ERROR_STATUS_OFFSET = 6'h 34; + parameter logic [BlockAw-1:0] SPI_HOST_EVENT_ENABLE_OFFSET = 6'h 38; // Reset values for hwext registers and their fields parameter logic [1:0] SPI_HOST_INTR_TEST_RESVAL = 2'h 0; @@ -324,12 +339,10 @@ package spi_host_reg_pkg; parameter logic [0:0] SPI_HOST_COMMAND_CSAAT_RESVAL = 1'h 0; parameter logic [1:0] SPI_HOST_COMMAND_SPEED_RESVAL = 2'h 0; parameter logic [1:0] SPI_HOST_COMMAND_DIRECTION_RESVAL = 2'h 0; - - // Window parameters - parameter logic [BlockAw-1:0] SPI_HOST_RXDATA_OFFSET = 6'h 24; - parameter int unsigned SPI_HOST_RXDATA_SIZE = 'h 4; - parameter logic [BlockAw-1:0] SPI_HOST_TXDATA_OFFSET = 6'h 28; - parameter int unsigned SPI_HOST_TXDATA_SIZE = 'h 4; + parameter logic [31:0] SPI_HOST_RXDATA_RESVAL = 32'h 0; + parameter logic [31:0] SPI_HOST_RXDATA_RXDATA_RESVAL = 32'h 0; + parameter logic [31:0] SPI_HOST_TXDATA_RESVAL = 32'h 0; + parameter logic [31:0] SPI_HOST_TXDATA_TXDATA_RESVAL = 32'h 0; // Register index typedef enum int { @@ -339,28 +352,34 @@ package spi_host_reg_pkg; SPI_HOST_ALERT_TEST, SPI_HOST_CONTROL, SPI_HOST_STATUS, - SPI_HOST_CONFIGOPTS, + SPI_HOST_CONFIGOPTS_0, + SPI_HOST_CONFIGOPTS_1, SPI_HOST_CSID, SPI_HOST_COMMAND, + SPI_HOST_RXDATA, + SPI_HOST_TXDATA, SPI_HOST_ERROR_ENABLE, SPI_HOST_ERROR_STATUS, SPI_HOST_EVENT_ENABLE } spi_host_id_e; // Register width information to check illegal writes - parameter logic [3:0] SPI_HOST_PERMIT [12] = '{ + parameter logic [3:0] SPI_HOST_PERMIT [15] = '{ 4'b 0001, // index[ 0] SPI_HOST_INTR_STATE 4'b 0001, // index[ 1] SPI_HOST_INTR_ENABLE 4'b 0001, // index[ 2] SPI_HOST_INTR_TEST 4'b 0001, // index[ 3] SPI_HOST_ALERT_TEST 4'b 1111, // index[ 4] SPI_HOST_CONTROL 4'b 1111, // index[ 5] SPI_HOST_STATUS - 4'b 1111, // index[ 6] SPI_HOST_CONFIGOPTS - 4'b 1111, // index[ 7] SPI_HOST_CSID - 4'b 0011, // index[ 8] SPI_HOST_COMMAND - 4'b 0001, // index[ 9] SPI_HOST_ERROR_ENABLE - 4'b 0001, // index[10] SPI_HOST_ERROR_STATUS - 4'b 0001 // index[11] SPI_HOST_EVENT_ENABLE + 4'b 1111, // index[ 6] SPI_HOST_CONFIGOPTS_0 + 4'b 1111, // index[ 7] SPI_HOST_CONFIGOPTS_1 + 4'b 1111, // index[ 8] SPI_HOST_CSID + 4'b 0011, // index[ 9] SPI_HOST_COMMAND + 4'b 1111, // index[10] SPI_HOST_RXDATA + 4'b 1111, // index[11] SPI_HOST_TXDATA + 4'b 0001, // index[12] SPI_HOST_ERROR_ENABLE + 4'b 0001, // index[13] SPI_HOST_ERROR_STATUS + 4'b 0001 // index[14] SPI_HOST_EVENT_ENABLE }; endpackage diff --git a/src/spi_host/rtl/spi_host_reg_top.sv b/src/spi_host/rtl/spi_host_reg_top.sv index 33718bb56..4774308ea 100644 --- a/src/spi_host/rtl/spi_host_reg_top.sv +++ b/src/spi_host/rtl/spi_host_reg_top.sv @@ -6,15 +6,27 @@ `include "prim_assert.sv" -module spi_host_reg_top ( +module spi_host_reg_top #( + parameter AHBDataWidth = 64, + parameter AHBAddrWidth = 32 +) ( input clk_i, input rst_ni, - input tlul_pkg::tl_h2d_t tl_i, - output tlul_pkg::tl_d2h_t tl_o, - // Output port for window - output tlul_pkg::tl_h2d_t tl_win_o [2], - input tlul_pkg::tl_d2h_t tl_win_i [2], + // AMBA AHB Lite Interface + input logic [AHBAddrWidth-1:0] haddr_i, + input logic [AHBDataWidth-1:0] hwdata_i, + input logic hsel_i, + input logic hwrite_i, + input logic hready_i, + input logic [1:0] htrans_i, + input logic [2:0] hsize_i, + + output logic hresp_o, + output logic hreadyout_o, + output logic [AHBDataWidth-1:0] hrdata_o, + + output logic fifo_rx_re, // To HW output spi_host_reg_pkg::spi_host_reg2hw_t reg2hw, // Write @@ -27,12 +39,20 @@ module spi_host_reg_top ( input devmode_i // If 1, explicit error return for unmapped register access ); - import spi_host_reg_pkg::* ; + import spi_host_reg_pkg::*; localparam int AW = 6; localparam int DW = 32; localparam int DBW = DW/8; // Byte Width + // ahb interface register signals + logic ahb_reg_dv; + logic ahb_reg_hld; + logic ahb_reg_err; + logic ahb_reg_write; + logic [DW-1:0] ahb_reg_wdata; + logic [AW-1:0] ahb_reg_addr; + logic [DW-1:0] ahb_reg_rdata; // register signals logic reg_we; logic reg_re; @@ -47,22 +67,11 @@ module spi_host_reg_top ( logic [DW-1:0] reg_rdata_next; logic reg_busy; - tlul_pkg::tl_h2d_t tl_reg_h2d; - tlul_pkg::tl_d2h_t tl_reg_d2h; - - - // incoming payload check - logic intg_err; - tlul_cmd_intg_chk u_chk ( - .tl_i(tl_i), - .err_o(intg_err) - ); - // also check for spurious write enables logic reg_we_err; - logic [11:0] reg_we_check; + logic [14:0] reg_we_check; prim_reg_we_check #( - .OneHotWidth(12) + .OneHotWidth(15) ) u_prim_reg_we_check ( .clk_i(clk_i), .rst_ni(rst_ni), @@ -75,103 +84,70 @@ module spi_host_reg_top ( always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin err_q <= '0; - end else if (intg_err || reg_we_err) begin + end else if (reg_we_err) begin err_q <= 1'b1; end end // integrity error output is permanent and should be used for alert generation // register errors are transactional - assign intg_err_o = err_q | intg_err | reg_we_err; - - // outgoing integrity generation - tlul_pkg::tl_d2h_t tl_o_pre; - tlul_rsp_intg_gen #( - .EnableRspIntgGen(1), - .EnableDataIntgGen(1) - ) u_rsp_intg_gen ( - .tl_i(tl_o_pre), - .tl_o(tl_o) - ); - - tlul_pkg::tl_h2d_t tl_socket_h2d [3]; - tlul_pkg::tl_d2h_t tl_socket_d2h [3]; - - logic [1:0] reg_steer; - - // socket_1n connection - assign tl_reg_h2d = tl_socket_h2d[2]; - assign tl_socket_d2h[2] = tl_reg_d2h; - - assign tl_win_o[0] = tl_socket_h2d[0]; - assign tl_socket_d2h[0] = tl_win_i[0]; - assign tl_win_o[1] = tl_socket_h2d[1]; - assign tl_socket_d2h[1] = tl_win_i[1]; - - // Create Socket_1n - tlul_socket_1n #( - .N (3), - .HReqPass (1'b1), - .HRspPass (1'b1), - .DReqPass ({3{1'b1}}), - .DRspPass ({3{1'b1}}), - .HReqDepth (4'h0), - .HRspDepth (4'h0), - .DReqDepth ({3{4'h0}}), - .DRspDepth ({3{4'h0}}), - .ExplicitErrs (1'b0) - ) u_socket ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .tl_h_i (tl_i), - .tl_h_o (tl_o_pre), - .tl_d_o (tl_socket_h2d), - .tl_d_i (tl_socket_d2h), - .dev_select_i (reg_steer) - ); - - // Create steering logic - always_comb begin - reg_steer = - tl_i.a_address[AW-1:0] inside {[36:39]} ? 2'd0 : - tl_i.a_address[AW-1:0] inside {[40:43]} ? 2'd1 : - // Default set to register - 2'd2; - - // Override this in case of an integrity error - if (intg_err) begin - reg_steer = 2'd2; - end - end - - tlul_adapter_reg #( - .RegAw(AW), - .RegDw(DW), - .EnableDataIntgGen(0) - ) u_reg_if ( - .clk_i (clk_i), - .rst_ni (rst_ni), - - .tl_i (tl_reg_h2d), - .tl_o (tl_reg_d2h), - - .en_ifetch_i(prim_mubi_pkg::MuBi4False), - .intg_error_o(), + assign intg_err_o = err_q | reg_we_err; + + ahb_slv_sif #( + .AHB_DATA_WIDTH (AHBDataWidth), + .AHB_ADDR_WIDTH (AHBAddrWidth), + .CLIENT_DATA_WIDTH (DW), + .CLIENT_ADDR_WIDTH (AW) + ) u_ahb_slv_sif ( + .hclk (clk_i), + .hreset_n (rst_ni), + .haddr_i (haddr_i), + .hwdata_i (hwdata_i), + .hsel_i (hsel_i), + .hwrite_i (hwrite_i), + .hready_i (hready_i), + .htrans_i (htrans_i), + .hsize_i (hsize_i), + .hresp_o (hresp_o), + .hreadyout_o (hreadyout_o), + .hrdata_o (hrdata_o), + //component inf + .dv (ahb_reg_dv), + .hld (ahb_reg_hld), + .err (ahb_reg_err), + .write (ahb_reg_write), + .wdata (ahb_reg_wdata), + .addr (ahb_reg_addr), + .rdata (ahb_reg_rdata) + ); - .we_o (reg_we), - .re_o (reg_re), - .addr_o (reg_addr), - .wdata_o (reg_wdata), - .be_o (reg_be), - .busy_i (reg_busy), - .rdata_i (reg_rdata), - .error_i (reg_error) + ahb_to_reg_adapter #( + .DATA_WIDTH (DW), + .ADDR_WIDTH (AW) + ) u_ahb_to_reg_adapter ( + .clk (clk_i), + .rst_n (rst_ni), + .ahb_reg_dv (ahb_reg_dv), + .ahb_reg_hld (ahb_reg_hld), + .ahb_reg_err (ahb_reg_err), + .ahb_reg_write (ahb_reg_write), + .ahb_reg_wdata (ahb_reg_wdata), + .ahb_reg_addr (ahb_reg_addr), + .ahb_reg_rdata (ahb_reg_rdata), + .reg_we (reg_we), + .reg_re (reg_re), + .reg_addr (reg_addr), + .reg_wdata (reg_wdata), + .reg_be (reg_be), + .reg_rdata (reg_rdata), + .reg_error (reg_error), + .reg_busy (reg_busy) ); // cdc oversampling signals - assign reg_rdata = reg_rdata_next ; - assign reg_error = (devmode_i & addrmiss) | wr_err | intg_err; + assign reg_rdata = reg_rdata_next; + assign reg_error = (devmode_i & addrmiss) | wr_err; // Define SW related signals // Format: __{wd|we|qs} @@ -216,21 +192,36 @@ module spi_host_reg_top ( logic status_txfull_qs; logic status_active_qs; logic status_ready_qs; - logic configopts_we; - logic [15:0] configopts_clkdiv_0_qs; - logic [15:0] configopts_clkdiv_0_wd; - logic [3:0] configopts_csnidle_0_qs; - logic [3:0] configopts_csnidle_0_wd; - logic [3:0] configopts_csntrail_0_qs; - logic [3:0] configopts_csntrail_0_wd; - logic [3:0] configopts_csnlead_0_qs; - logic [3:0] configopts_csnlead_0_wd; - logic configopts_fullcyc_0_qs; - logic configopts_fullcyc_0_wd; - logic configopts_cpha_0_qs; - logic configopts_cpha_0_wd; - logic configopts_cpol_0_qs; - logic configopts_cpol_0_wd; + logic configopts_0_we; + logic [15:0] configopts_0_clkdiv_0_qs; + logic [15:0] configopts_0_clkdiv_0_wd; + logic [3:0] configopts_0_csnidle_0_qs; + logic [3:0] configopts_0_csnidle_0_wd; + logic [3:0] configopts_0_csntrail_0_qs; + logic [3:0] configopts_0_csntrail_0_wd; + logic [3:0] configopts_0_csnlead_0_qs; + logic [3:0] configopts_0_csnlead_0_wd; + logic configopts_0_fullcyc_0_qs; + logic configopts_0_fullcyc_0_wd; + logic configopts_0_cpha_0_qs; + logic configopts_0_cpha_0_wd; + logic configopts_0_cpol_0_qs; + logic configopts_0_cpol_0_wd; + logic configopts_1_we; + logic [15:0] configopts_1_clkdiv_1_qs; + logic [15:0] configopts_1_clkdiv_1_wd; + logic [3:0] configopts_1_csnidle_1_qs; + logic [3:0] configopts_1_csnidle_1_wd; + logic [3:0] configopts_1_csntrail_1_qs; + logic [3:0] configopts_1_csntrail_1_wd; + logic [3:0] configopts_1_csnlead_1_qs; + logic [3:0] configopts_1_csnlead_1_wd; + logic configopts_1_fullcyc_1_qs; + logic configopts_1_fullcyc_1_wd; + logic configopts_1_cpha_1_qs; + logic configopts_1_cpha_1_wd; + logic configopts_1_cpol_1_qs; + logic configopts_1_cpol_1_wd; logic csid_we; logic [31:0] csid_qs; logic [31:0] csid_wd; @@ -239,6 +230,10 @@ module spi_host_reg_top ( logic command_csaat_wd; logic [1:0] command_speed_wd; logic [1:0] command_direction_wd; + logic [31:0] rxdata_qs; + logic rxdata_re; + logic txdata_we; + logic [31:0] txdata_wd; logic error_enable_we; logic error_enable_cmdbusy_qs; logic error_enable_cmdbusy_wd; @@ -942,19 +937,19 @@ module spi_host_reg_top ( // Subregister 0 of Multireg configopts - // R[configopts]: V(False) + // R[configopts_0]: V(False) // F[clkdiv_0]: 15:0 prim_subreg #( .DW (16), .SwAccess(prim_subreg_pkg::SwAccessRW), .RESVAL (16'h0) - ) u_configopts_clkdiv_0 ( + ) u_configopts_0_clkdiv_0 ( .clk_i (clk_i), .rst_ni (rst_ni), // from register interface - .we (configopts_we), - .wd (configopts_clkdiv_0_wd), + .we (configopts_0_we), + .wd (configopts_0_clkdiv_0_wd), // from internal hardware .de (1'b0), @@ -966,7 +961,7 @@ module spi_host_reg_top ( .ds (), // to register interface (read) - .qs (configopts_clkdiv_0_qs) + .qs (configopts_0_clkdiv_0_qs) ); // F[csnidle_0]: 19:16 @@ -974,13 +969,13 @@ module spi_host_reg_top ( .DW (4), .SwAccess(prim_subreg_pkg::SwAccessRW), .RESVAL (4'h0) - ) u_configopts_csnidle_0 ( + ) u_configopts_0_csnidle_0 ( .clk_i (clk_i), .rst_ni (rst_ni), // from register interface - .we (configopts_we), - .wd (configopts_csnidle_0_wd), + .we (configopts_0_we), + .wd (configopts_0_csnidle_0_wd), // from internal hardware .de (1'b0), @@ -992,7 +987,7 @@ module spi_host_reg_top ( .ds (), // to register interface (read) - .qs (configopts_csnidle_0_qs) + .qs (configopts_0_csnidle_0_qs) ); // F[csntrail_0]: 23:20 @@ -1000,13 +995,13 @@ module spi_host_reg_top ( .DW (4), .SwAccess(prim_subreg_pkg::SwAccessRW), .RESVAL (4'h0) - ) u_configopts_csntrail_0 ( + ) u_configopts_0_csntrail_0 ( .clk_i (clk_i), .rst_ni (rst_ni), // from register interface - .we (configopts_we), - .wd (configopts_csntrail_0_wd), + .we (configopts_0_we), + .wd (configopts_0_csntrail_0_wd), // from internal hardware .de (1'b0), @@ -1018,7 +1013,7 @@ module spi_host_reg_top ( .ds (), // to register interface (read) - .qs (configopts_csntrail_0_qs) + .qs (configopts_0_csntrail_0_qs) ); // F[csnlead_0]: 27:24 @@ -1026,13 +1021,13 @@ module spi_host_reg_top ( .DW (4), .SwAccess(prim_subreg_pkg::SwAccessRW), .RESVAL (4'h0) - ) u_configopts_csnlead_0 ( + ) u_configopts_0_csnlead_0 ( .clk_i (clk_i), .rst_ni (rst_ni), // from register interface - .we (configopts_we), - .wd (configopts_csnlead_0_wd), + .we (configopts_0_we), + .wd (configopts_0_csnlead_0_wd), // from internal hardware .de (1'b0), @@ -1044,7 +1039,7 @@ module spi_host_reg_top ( .ds (), // to register interface (read) - .qs (configopts_csnlead_0_qs) + .qs (configopts_0_csnlead_0_qs) ); // F[fullcyc_0]: 29:29 @@ -1052,13 +1047,13 @@ module spi_host_reg_top ( .DW (1), .SwAccess(prim_subreg_pkg::SwAccessRW), .RESVAL (1'h0) - ) u_configopts_fullcyc_0 ( + ) u_configopts_0_fullcyc_0 ( .clk_i (clk_i), .rst_ni (rst_ni), // from register interface - .we (configopts_we), - .wd (configopts_fullcyc_0_wd), + .we (configopts_0_we), + .wd (configopts_0_fullcyc_0_wd), // from internal hardware .de (1'b0), @@ -1070,7 +1065,7 @@ module spi_host_reg_top ( .ds (), // to register interface (read) - .qs (configopts_fullcyc_0_qs) + .qs (configopts_0_fullcyc_0_qs) ); // F[cpha_0]: 30:30 @@ -1078,13 +1073,13 @@ module spi_host_reg_top ( .DW (1), .SwAccess(prim_subreg_pkg::SwAccessRW), .RESVAL (1'h0) - ) u_configopts_cpha_0 ( + ) u_configopts_0_cpha_0 ( .clk_i (clk_i), .rst_ni (rst_ni), // from register interface - .we (configopts_we), - .wd (configopts_cpha_0_wd), + .we (configopts_0_we), + .wd (configopts_0_cpha_0_wd), // from internal hardware .de (1'b0), @@ -1096,7 +1091,7 @@ module spi_host_reg_top ( .ds (), // to register interface (read) - .qs (configopts_cpha_0_qs) + .qs (configopts_0_cpha_0_qs) ); // F[cpol_0]: 31:31 @@ -1104,13 +1099,13 @@ module spi_host_reg_top ( .DW (1), .SwAccess(prim_subreg_pkg::SwAccessRW), .RESVAL (1'h0) - ) u_configopts_cpol_0 ( + ) u_configopts_0_cpol_0 ( .clk_i (clk_i), .rst_ni (rst_ni), // from register interface - .we (configopts_we), - .wd (configopts_cpol_0_wd), + .we (configopts_0_we), + .wd (configopts_0_cpol_0_wd), // from internal hardware .de (1'b0), @@ -1122,7 +1117,192 @@ module spi_host_reg_top ( .ds (), // to register interface (read) - .qs (configopts_cpol_0_qs) + .qs (configopts_0_cpol_0_qs) + ); + + + // Subregister 1 of Multireg configopts + // R[configopts_1]: V(False) + // F[clkdiv_1]: 15:0 + prim_subreg #( + .DW (16), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (16'h0) + ) u_configopts_1_clkdiv_1 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (configopts_1_we), + .wd (configopts_1_clkdiv_1_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.configopts[1].clkdiv.q), + .ds (), + + // to register interface (read) + .qs (configopts_1_clkdiv_1_qs) + ); + + // F[csnidle_1]: 19:16 + prim_subreg #( + .DW (4), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (4'h0) + ) u_configopts_1_csnidle_1 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (configopts_1_we), + .wd (configopts_1_csnidle_1_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.configopts[1].csnidle.q), + .ds (), + + // to register interface (read) + .qs (configopts_1_csnidle_1_qs) + ); + + // F[csntrail_1]: 23:20 + prim_subreg #( + .DW (4), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (4'h0) + ) u_configopts_1_csntrail_1 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (configopts_1_we), + .wd (configopts_1_csntrail_1_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.configopts[1].csntrail.q), + .ds (), + + // to register interface (read) + .qs (configopts_1_csntrail_1_qs) + ); + + // F[csnlead_1]: 27:24 + prim_subreg #( + .DW (4), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (4'h0) + ) u_configopts_1_csnlead_1 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (configopts_1_we), + .wd (configopts_1_csnlead_1_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.configopts[1].csnlead.q), + .ds (), + + // to register interface (read) + .qs (configopts_1_csnlead_1_qs) + ); + + // F[fullcyc_1]: 29:29 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (1'h0) + ) u_configopts_1_fullcyc_1 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (configopts_1_we), + .wd (configopts_1_fullcyc_1_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.configopts[1].fullcyc.q), + .ds (), + + // to register interface (read) + .qs (configopts_1_fullcyc_1_qs) + ); + + // F[cpha_1]: 30:30 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (1'h0) + ) u_configopts_1_cpha_1 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (configopts_1_we), + .wd (configopts_1_cpha_1_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.configopts[1].cpha.q), + .ds (), + + // to register interface (read) + .qs (configopts_1_cpha_1_qs) + ); + + // F[cpol_1]: 31:31 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (1'h0) + ) u_configopts_1_cpol_1 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (configopts_1_we), + .wd (configopts_1_cpol_1_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.configopts[1].cpol.q), + .ds (), + + // to register interface (read) + .qs (configopts_1_cpol_1_qs) ); @@ -1222,6 +1402,53 @@ module spi_host_reg_top ( assign reg2hw.command.direction.qe = command_qe; + // R[rxdata]: V(False) + prim_subreg #( + .DW (32), + .SwAccess(prim_subreg_pkg::SwAccessRO), + .RESVAL (32'h0) + ) u_rxdata ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (1'b0), + .wd ('0), + + // from internal hardware + .de (hw2reg.rxdata.de), + .d (hw2reg.rxdata.d), + + // to internal hardware + .qe (), + .q (), + .ds (), + + // to register interface (read) + .qs (rxdata_qs) + ); + assign fifo_rx_re = rxdata_re; + + // R[txdata]: V(True) + logic txdata_qe; + logic [0:0] txdata_flds_we; + assign txdata_qe = &txdata_flds_we; + prim_subreg_ext #( + .DW (32) + ) u_txdata ( + .re (1'b0), + .we (txdata_we), + .wd (txdata_wd), + .d ('0), + .qre (), + .qe (txdata_flds_we[0]), + .q (reg2hw.txdata.q), + .ds (), + .qs () + ); + assign reg2hw.txdata.qe = txdata_qe; + + // R[error_enable]: V(False) // F[cmdbusy]: 0:0 prim_subreg #( @@ -1671,7 +1898,7 @@ module spi_host_reg_top ( - logic [11:0] addr_hit; + logic [14:0] addr_hit; always_comb begin addr_hit = '0; addr_hit[ 0] = (reg_addr == SPI_HOST_INTR_STATE_OFFSET); @@ -1680,15 +1907,18 @@ module spi_host_reg_top ( addr_hit[ 3] = (reg_addr == SPI_HOST_ALERT_TEST_OFFSET); addr_hit[ 4] = (reg_addr == SPI_HOST_CONTROL_OFFSET); addr_hit[ 5] = (reg_addr == SPI_HOST_STATUS_OFFSET); - addr_hit[ 6] = (reg_addr == SPI_HOST_CONFIGOPTS_OFFSET); - addr_hit[ 7] = (reg_addr == SPI_HOST_CSID_OFFSET); - addr_hit[ 8] = (reg_addr == SPI_HOST_COMMAND_OFFSET); - addr_hit[ 9] = (reg_addr == SPI_HOST_ERROR_ENABLE_OFFSET); - addr_hit[10] = (reg_addr == SPI_HOST_ERROR_STATUS_OFFSET); - addr_hit[11] = (reg_addr == SPI_HOST_EVENT_ENABLE_OFFSET); + addr_hit[ 6] = (reg_addr == SPI_HOST_CONFIGOPTS_0_OFFSET); + addr_hit[ 7] = (reg_addr == SPI_HOST_CONFIGOPTS_1_OFFSET); + addr_hit[ 8] = (reg_addr == SPI_HOST_CSID_OFFSET); + addr_hit[ 9] = (reg_addr == SPI_HOST_COMMAND_OFFSET); + addr_hit[10] = (reg_addr == SPI_HOST_RXDATA_OFFSET); + addr_hit[11] = (reg_addr == SPI_HOST_TXDATA_OFFSET); + addr_hit[12] = (reg_addr == SPI_HOST_ERROR_ENABLE_OFFSET); + addr_hit[13] = (reg_addr == SPI_HOST_ERROR_STATUS_OFFSET); + addr_hit[14] = (reg_addr == SPI_HOST_EVENT_ENABLE_OFFSET); end - assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0; // Check sub-word write is permitted always_comb begin @@ -1704,7 +1934,10 @@ module spi_host_reg_top ( (addr_hit[ 8] & (|(SPI_HOST_PERMIT[ 8] & ~reg_be))) | (addr_hit[ 9] & (|(SPI_HOST_PERMIT[ 9] & ~reg_be))) | (addr_hit[10] & (|(SPI_HOST_PERMIT[10] & ~reg_be))) | - (addr_hit[11] & (|(SPI_HOST_PERMIT[11] & ~reg_be))))); + (addr_hit[11] & (|(SPI_HOST_PERMIT[11] & ~reg_be))) | + (addr_hit[12] & (|(SPI_HOST_PERMIT[12] & ~reg_be))) | + (addr_hit[13] & (|(SPI_HOST_PERMIT[13] & ~reg_be))) | + (addr_hit[14] & (|(SPI_HOST_PERMIT[14] & ~reg_be))))); end // Generate write-enables @@ -1737,25 +1970,40 @@ module spi_host_reg_top ( assign control_sw_rst_wd = reg_wdata[30]; assign control_spien_wd = reg_wdata[31]; - assign configopts_we = addr_hit[6] & reg_we & !reg_error; + assign configopts_0_we = addr_hit[6] & reg_we & !reg_error; + + assign configopts_0_clkdiv_0_wd = reg_wdata[15:0]; + + assign configopts_0_csnidle_0_wd = reg_wdata[19:16]; + + assign configopts_0_csntrail_0_wd = reg_wdata[23:20]; + + assign configopts_0_csnlead_0_wd = reg_wdata[27:24]; + + assign configopts_0_fullcyc_0_wd = reg_wdata[29]; - assign configopts_clkdiv_0_wd = reg_wdata[15:0]; + assign configopts_0_cpha_0_wd = reg_wdata[30]; - assign configopts_csnidle_0_wd = reg_wdata[19:16]; + assign configopts_0_cpol_0_wd = reg_wdata[31]; + assign configopts_1_we = addr_hit[7] & reg_we & !reg_error; - assign configopts_csntrail_0_wd = reg_wdata[23:20]; + assign configopts_1_clkdiv_1_wd = reg_wdata[15:0]; - assign configopts_csnlead_0_wd = reg_wdata[27:24]; + assign configopts_1_csnidle_1_wd = reg_wdata[19:16]; - assign configopts_fullcyc_0_wd = reg_wdata[29]; + assign configopts_1_csntrail_1_wd = reg_wdata[23:20]; - assign configopts_cpha_0_wd = reg_wdata[30]; + assign configopts_1_csnlead_1_wd = reg_wdata[27:24]; - assign configopts_cpol_0_wd = reg_wdata[31]; - assign csid_we = addr_hit[7] & reg_we & !reg_error; + assign configopts_1_fullcyc_1_wd = reg_wdata[29]; + + assign configopts_1_cpha_1_wd = reg_wdata[30]; + + assign configopts_1_cpol_1_wd = reg_wdata[31]; + assign csid_we = addr_hit[8] & reg_we & !reg_error; assign csid_wd = reg_wdata[31:0]; - assign command_we = addr_hit[8] & reg_we & !reg_error; + assign command_we = addr_hit[9] & reg_we & !reg_error; assign command_len_wd = reg_wdata[8:0]; @@ -1764,7 +2012,11 @@ module spi_host_reg_top ( assign command_speed_wd = reg_wdata[11:10]; assign command_direction_wd = reg_wdata[13:12]; - assign error_enable_we = addr_hit[9] & reg_we & !reg_error; + assign rxdata_re = addr_hit[10] & reg_re & !reg_error; + assign txdata_we = addr_hit[11] & reg_we & !reg_error; + + assign txdata_wd = reg_wdata[31:0]; + assign error_enable_we = addr_hit[12] & reg_we & !reg_error; assign error_enable_cmdbusy_wd = reg_wdata[0]; @@ -1775,7 +2027,7 @@ module spi_host_reg_top ( assign error_enable_cmdinval_wd = reg_wdata[3]; assign error_enable_csidinval_wd = reg_wdata[4]; - assign error_status_we = addr_hit[10] & reg_we & !reg_error; + assign error_status_we = addr_hit[13] & reg_we & !reg_error; assign error_status_cmdbusy_wd = reg_wdata[0]; @@ -1788,7 +2040,7 @@ module spi_host_reg_top ( assign error_status_csidinval_wd = reg_wdata[4]; assign error_status_accessinval_wd = reg_wdata[5]; - assign event_enable_we = addr_hit[11] & reg_we & !reg_error; + assign event_enable_we = addr_hit[14] & reg_we & !reg_error; assign event_enable_rxfull_wd = reg_wdata[0]; @@ -1811,12 +2063,15 @@ module spi_host_reg_top ( reg_we_check[3] = alert_test_we; reg_we_check[4] = control_we; reg_we_check[5] = 1'b0; - reg_we_check[6] = configopts_we; - reg_we_check[7] = csid_we; - reg_we_check[8] = command_we; - reg_we_check[9] = error_enable_we; - reg_we_check[10] = error_status_we; - reg_we_check[11] = event_enable_we; + reg_we_check[6] = configopts_0_we; + reg_we_check[7] = configopts_1_we; + reg_we_check[8] = csid_we; + reg_we_check[9] = command_we; + reg_we_check[10] = 1'b0; + reg_we_check[11] = txdata_we; + reg_we_check[12] = error_enable_we; + reg_we_check[13] = error_status_we; + reg_we_check[14] = event_enable_we; end // Read data return @@ -1868,27 +2123,45 @@ module spi_host_reg_top ( end addr_hit[6]: begin - reg_rdata_next[15:0] = configopts_clkdiv_0_qs; - reg_rdata_next[19:16] = configopts_csnidle_0_qs; - reg_rdata_next[23:20] = configopts_csntrail_0_qs; - reg_rdata_next[27:24] = configopts_csnlead_0_qs; - reg_rdata_next[29] = configopts_fullcyc_0_qs; - reg_rdata_next[30] = configopts_cpha_0_qs; - reg_rdata_next[31] = configopts_cpol_0_qs; + reg_rdata_next[15:0] = configopts_0_clkdiv_0_qs; + reg_rdata_next[19:16] = configopts_0_csnidle_0_qs; + reg_rdata_next[23:20] = configopts_0_csntrail_0_qs; + reg_rdata_next[27:24] = configopts_0_csnlead_0_qs; + reg_rdata_next[29] = configopts_0_fullcyc_0_qs; + reg_rdata_next[30] = configopts_0_cpha_0_qs; + reg_rdata_next[31] = configopts_0_cpol_0_qs; end addr_hit[7]: begin - reg_rdata_next[31:0] = csid_qs; + reg_rdata_next[15:0] = configopts_1_clkdiv_1_qs; + reg_rdata_next[19:16] = configopts_1_csnidle_1_qs; + reg_rdata_next[23:20] = configopts_1_csntrail_1_qs; + reg_rdata_next[27:24] = configopts_1_csnlead_1_qs; + reg_rdata_next[29] = configopts_1_fullcyc_1_qs; + reg_rdata_next[30] = configopts_1_cpha_1_qs; + reg_rdata_next[31] = configopts_1_cpol_1_qs; end addr_hit[8]: begin + reg_rdata_next[31:0] = csid_qs; + end + + addr_hit[9]: begin reg_rdata_next[8:0] = '0; reg_rdata_next[9] = '0; reg_rdata_next[11:10] = '0; reg_rdata_next[13:12] = '0; end - addr_hit[9]: begin + addr_hit[10]: begin + reg_rdata_next[31:0] = rxdata_qs; + end + + addr_hit[11]: begin + reg_rdata_next[31:0] = '0; + end + + addr_hit[12]: begin reg_rdata_next[0] = error_enable_cmdbusy_qs; reg_rdata_next[1] = error_enable_overflow_qs; reg_rdata_next[2] = error_enable_underflow_qs; @@ -1896,7 +2169,7 @@ module spi_host_reg_top ( reg_rdata_next[4] = error_enable_csidinval_qs; end - addr_hit[10]: begin + addr_hit[13]: begin reg_rdata_next[0] = error_status_cmdbusy_qs; reg_rdata_next[1] = error_status_overflow_qs; reg_rdata_next[2] = error_status_underflow_qs; @@ -1905,7 +2178,7 @@ module spi_host_reg_top ( reg_rdata_next[5] = error_status_accessinval_qs; end - addr_hit[11]: begin + addr_hit[14]: begin reg_rdata_next[0] = event_enable_rxfull_qs; reg_rdata_next[1] = event_enable_txempty_qs; reg_rdata_next[2] = event_enable_rxwm_qs; @@ -1937,15 +2210,11 @@ module spi_host_reg_top ( assign unused_be = ^reg_be; // Assertions for Register Interface - `ASSERT_PULSE(wePulse, reg_we, clk_i, !rst_ni) - `ASSERT_PULSE(rePulse, reg_re, clk_i, !rst_ni) - - `ASSERT(reAfterRv, $rose(reg_re || reg_we) |=> tl_o_pre.d_valid, clk_i, !rst_ni) + `CALIPTRA_ASSERT_PULSE(wePulse, reg_we, clk_i, !rst_ni) + `CALIPTRA_ASSERT_PULSE(rePulse, reg_re, clk_i, !rst_ni) - `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit), clk_i, !rst_ni) + `CALIPTRA_ASSERT(reAfterRv, $rose(reg_re || reg_we) |=> hreadyout_o, clk_i, !rst_ni) - // this is formulated as an assumption such that the FPV testbenches do disprove this - // property by mistake - //`ASSUME(reqParity, tl_reg_h2d.a_valid |-> tl_reg_h2d.a_user.chk_en == tlul_pkg::CheckDis) + `CALIPTRA_ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit), clk_i, !rst_ni) endmodule diff --git a/src/spi_host/rtl/spi_host_shift_register.sv b/src/spi_host/rtl/spi_host_shift_register.sv index ca98e9e48..2a09c28ff 100644 --- a/src/spi_host/rtl/spi_host_shift_register.sv +++ b/src/spi_host/rtl/spi_host_shift_register.sv @@ -51,7 +51,7 @@ module spi_host_shift_register ( logic rx_buf_valid_q; logic rx_buf_valid_d; - `ASSERT(SpeedValid, speed_i != RsvdSpd, clk_i, rst_ni) + `CALIPTRA_ASSERT(SpeedValid, speed_i != RsvdSpd, clk_i, rst_ni) assign next_bits = full_cyc_i ? sd_i : sd_i_q; assign sr_shifted = (speed_i == Standard) ? {sr_q[6:0], next_bits[1]} : diff --git a/src/spi_host/rtl/spi_host_window.sv b/src/spi_host/rtl/spi_host_window.sv deleted file mode 100644 index a532e4753..000000000 --- a/src/spi_host/rtl/spi_host_window.sv +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright lowRISC contributors. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 -// -// Module to manage TX FIFO window for Serial Peripheral Interface (SPI) host IP. -// - -module spi_host_window ( - input clk_i, - input rst_ni, - input tlul_pkg::tl_h2d_t rx_win_i, - output tlul_pkg::tl_d2h_t rx_win_o, - input tlul_pkg::tl_h2d_t tx_win_i, - output tlul_pkg::tl_d2h_t tx_win_o, - output logic [31:0] tx_data_o, - output logic [3:0] tx_be_o, - output logic tx_valid_o, - input [31:0] rx_data_i, - output logic rx_ready_o -); - - localparam int AW = spi_host_reg_pkg::BlockAw; - localparam int DW = 32; - localparam int ByteMaskW = DW / 8; - - logic rx_we; - - // Only support reads from the data RX fifo window - logic rx_access_error; - assign rx_access_error = rx_we; - - tlul_adapter_reg #( - .RegAw (AW), - .RegDw (DW) - ) u_adapter_rx ( - .clk_i, - .rst_ni, - .tl_i (rx_win_i), - .tl_o (rx_win_o), - .en_ifetch_i (prim_mubi_pkg::MuBi4False), - .intg_error_o(), - .we_o (rx_we), - .re_o (rx_ready_o), - .addr_o (), - .wdata_o (), - .be_o (), - .rdata_i (rx_data_i), - .error_i (rx_access_error), - .busy_i ('0) - ); - - // translate bitmask to byte mask - logic [DW-1:0] bit_mask; - for (genvar i = 0; i < ByteMaskW; i++) begin : gen_byte_mask - assign tx_be_o[i] = |bit_mask[i*8 +: 8]; - - // all the bits of particular byte must be the same - `ASSERT(BitMaskCheck_A, (|bit_mask[i*8 +: 8] == 1'b0) || - (&bit_mask[i*8 +: 8] == 1'b1)) - end - - // Only support writes to the data TX fifo window - tlul_adapter_sram #( - .SramAw(AW), - .SramDw(DW), - .Outstanding(1), - .ByteAccess(1), - .ErrOnWrite(0), - .ErrOnRead(1) - ) u_adapter_tx ( - .clk_i, - .rst_ni, - .tl_i(tx_win_i), - .tl_o(tx_win_o), - .en_ifetch_i(prim_mubi_pkg::MuBi4False), - .req_o(tx_valid_o), - .req_type_o(), - .gnt_i(1'b1), - .we_o(), - .addr_o(), - .wdata_o(tx_data_o), - .wmask_o(bit_mask), - .intg_error_o(), - .rdata_i('0), - .rvalid_i('0), - .rerror_i('0) - ); - -endmodule : spi_host_window diff --git a/src/spi_host/tb/Makefile b/src/spi_host/tb/Makefile new file mode 100644 index 000000000..e65bb88cb --- /dev/null +++ b/src/spi_host/tb/Makefile @@ -0,0 +1,41 @@ +VERILATOR = verilator + +IPNAME = spi_host +IPDIR = $(CALIPTRA_ROOT)/src/$(IPNAME) +TBFILES = $(IPDIR)/config/$(IPNAME).vf +VERILATOR_LINT_FILES = $(IPDIR)/config/$(IPNAME).vlt + +# Optimization for better performance; alternative is nothing for +# slower runtime (faster compiles) -O2 for faster runtime (slower +# compiles), or -O for balance. +VERILATOR_MAKE_FLAGS = OPT_FAST="-Os" + +# Targets +all: clean verilator + +clean: + rm -rf *.log *.s *.hex *.dis *.size *.tbl irun* vcs* simv* .map *.map snapshots \ + verilator* *.exe obj* *.o ucli.key vc_hdrs.h csrc *.csv work \ + dataset.asdb library.cfg vsimsa.cfg riviera-build wave.asdb sim.vcd \ + *.h + +############ Model Builds ############################### + +verilator-build: $(TBFILES) $(VERILATOR_LINT_FILES) test_$(IPNAME)_tb.cpp + $(VERILATOR) --cc \ + --timing \ + --timescale 1ns/1ps \ + -f $(TBFILES) --top-module $(IPNAME)_tb \ + -f $(VERILATOR_LINT_FILES) \ + -exe test_$(IPNAME)_tb.cpp \ + --trace-fst --trace-structs + cp test_$(IPNAME)_tb.cpp obj_dir/ + $(MAKE) -j -e -C obj_dir/ -f V$(IPNAME)_tb.mk $(VERILATOR_MAKE_FLAGS) VM_PARALLEL_BUILDS=1 + touch verilator-build + +############ TEST Simulation ############################### + +verilator: verilator-build + ./obj_dir/V$(IPNAME)_tb + +run: verilator diff --git a/src/spi_host/tb/spi_device_pkg.sv b/src/spi_host/tb/spi_device_pkg.sv new file mode 100644 index 000000000..24e925f8d --- /dev/null +++ b/src/spi_host/tb/spi_device_pkg.sv @@ -0,0 +1,53 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Serial Peripheral Interface (SPI) Device module. +// + +package spi_device_pkg; + + typedef logic [7:0] spi_byte_t; + + typedef enum spi_byte_t { + CmdNone = 8'h 00, + + CmdWriteStatus1 = 8'h 01, + CmdWriteStatus2 = 8'h 31, + CmdWriteStatus3 = 8'h 11, + + CmdReadStatus1 = 8'h 05, + CmdReadStatus2 = 8'h 35, + CmdReadStatus3 = 8'h 15, + + CmdJedecId = 8'h 9F, + + CmdPageProgram = 8'h 02, + CmdQuadPageProgram = 8'h 32, // Not supported + + CmdSectorErase = 8'h 20, // 4kB erase + CmdBlockErase32 = 8'h 52, // 32kB + CmdBlockErase64 = 8'h D8, // 64kB + + CmdReadData = 8'h 03, + CmdReadFast = 8'h 0B, // with Dummy + CmdReadDual = 8'h 3B, + CmdReadQuad = 8'h 6B, + CmdReadDualIO = 8'h BB, + CmdReadQuadIO = 8'h EB, + + CmdWriteDisable = 8'h 04, + CmdWriteEnable = 8'h 06, + + CmdEn4B = 8'h B7, + CmdEx4B = 8'h E9, + + CmdReadSfdp = 8'h 5A, + + CmdChipErase = 8'h C7, + + CmdEnableReset = 8'h 66, + CmdResetDevice = 8'h 99 + } spi_cmd_e; + +endpackage : spi_device_pkg diff --git a/src/spi_host/tb/spi_host_tb.sv b/src/spi_host/tb/spi_host_tb.sv new file mode 100644 index 000000000..677285648 --- /dev/null +++ b/src/spi_host/tb/spi_host_tb.sv @@ -0,0 +1,611 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//====================================================================== +// +// spi_host_tb.sv +// -------- +// Integration test bench to ensure read/write through AHB is working +// with spi_host ip. +// +//====================================================================== + +module spi_host_tb + import spi_host_reg_pkg::*; + import spi_device_pkg::*; + import prim_mubi_pkg::mubi8_t; +( +`ifdef VERILATOR + input bit clk_tb +`endif + ); + + //---------------------------------------------------------------- + // Internal constant and parameter definitions. + //---------------------------------------------------------------- + parameter DEBUG = 0; + parameter NUM_HOSTS = 2; + + parameter CLK_HALF_PERIOD = 2; + + parameter AHB_HTRANS_IDLE = 0; + parameter AHB_HTRANS_BUSY = 1; + parameter AHB_HTRANS_NONSEQ = 2; + parameter AHB_HTRANS_SEQ = 3; + + parameter AHB_ADDR_WIDTH = 32; + parameter AHB_DATA_WIDTH = 32; + + typedef struct packed { + logic ready; // spi host ready to receive command + logic active; // processing previously issued command + logic txfull; // tx fifo is full + logic txempty; // tx fifo is empty + logic txstall; // transaction has stalled due to lack of data in the tx fifo + logic txwm; // tx watermark + logic rxfull; // rx fifo is full + logic rxempty; // rx fifo is empty + logic rxstall; // ongoing transaction stalled due to lack of space in rx fifo + logic byteorder; // value of ByteOrder parameter + logic reserved; + logic rxwm; // rx watermark + logic [3:0] cmdqd; // command queue depth + logic [7:0] rxqd; // receive queue depth + logic [7:0] txqd; // transmit queue depth + } spi_host_status_t; + + //---------------------------------------------------------------- + // Register and Wire declarations. + //---------------------------------------------------------------- + reg [31 : 0] cycle_ctr; + reg [31 : 0] error_ctr; + reg [31 : 0] tc_ctr; + logic generate_rng; + + +`ifndef VERILATOR + reg clk_tb; +`endif + reg reset_n_tb; + + reg [AHB_ADDR_WIDTH-1:0] haddr_i_tb; + reg [AHB_DATA_WIDTH-1:0] hwdata_i_tb; + reg hsel_i_tb; + reg hwrite_i_tb; + reg hready_i_tb; + reg [1:0] htrans_i_tb; + reg [2:0] hsize_i_tb; + + wire hresp_o_tb; + wire hreadyout_o_tb; + logic [AHB_DATA_WIDTH-1:0] hrdata_o_tb; + + reg [31 : 0] read_data; + reg [255 : 0] digest_data; + + //---------------------------------------------------------------- + // Device Under Test. + //---------------------------------------------------------------- + wire [3:0] spi_flash_sd; + logic spi_flash_sck; + logic [1:0] spi_flash_csb; + + logic cio_sck_o; + logic cio_sck_en_o; + logic [NumCS-1:0] cio_csb_o; + logic [NumCS-1:0] cio_csb_en_o; + logic [3:0] cio_sd_o; + logic [3:0] cio_sd_en_o; + logic [3:0] cio_sd_i; + + assign spi_flash_sck = cio_sck_en_o ? cio_sck_o : 1'b0; + + for (genvar ii = 0; ii < NumCS; ii += 1) begin : gen_qspi_csb + assign spi_flash_csb[ii] = cio_csb_en_o[ii] ? cio_csb_o[ii] : 1'b1; // leave all high + end + + for (genvar ii = 0; ii < 4; ii += 1 ) begin : gen_qspi_sd + assign spi_flash_sd[ii] = cio_sd_en_o[ii] ? cio_sd_o[ii] : 1'bz; + assign cio_sd_i[ii] = cio_sd_en_o[ii] ? 1'bz : spi_flash_sd[ii]; + end + + spi_host #( + .AHBDataWidth(32), + .AHBAddrWidth(32) + ) dut ( + .clk_i (clk_tb), + .rst_ni (reset_n_tb), + + // AMBA AHB Lite Interface + .haddr_i (haddr_i_tb), + .hwdata_i (hwdata_i_tb), + .hsel_i (hsel_i_tb), + .hwrite_i (hwrite_i_tb), + .hready_i (hready_i_tb), + .htrans_i (htrans_i_tb), + .hsize_i (hsize_i_tb), + .hresp_o (hresp_o_tb), + .hreadyout_o (hreadyout_o_tb), + .hrdata_o (hrdata_o_tb), + + // Alerts + .alert_rx_i(prim_alert_pkg::ALERT_RX_DEFAULT), + .alert_tx_o(), + + // SPI Interface + .cio_sck_o (cio_sck_o), + .cio_sck_en_o (cio_sck_en_o), + .cio_csb_o (cio_csb_o), + .cio_csb_en_o (cio_csb_en_o), + .cio_sd_o (cio_sd_o), + .cio_sd_en_o (cio_sd_en_o), + .cio_sd_i (cio_sd_i), + + .intr_error_o(), + .intr_spi_event_o() + ); + + localparam logic [15:0] DeviceId0 = 16'hBA5E; + localparam logic [15:0] DeviceId1 = 16'h5AFE; + + spiflash #( + .DeviceId(DeviceId0) + ) spiflash0 ( + .sck (spi_flash_sck), + .csb (spi_flash_csb[0]), + .sd (spi_flash_sd) + ); + + spiflash #( + .DeviceId(DeviceId1) + ) spiflash1 ( + .sck (spi_flash_sck), + .csb (spi_flash_csb[1]), + .sd (spi_flash_sd) + ); + + //---------------------------------------------------------------- + // clk_gen + // + // Clock generator process. + //---------------------------------------------------------------- +`ifndef VERILATOR + always + begin : clk_gen + #CLK_HALF_PERIOD + clk_tb = !clk_tb; + end // clk_gen +`endif + + //---------------------------------------------------------------- + // sys_monitor + // + // Generates a cycle counter and displays information about + // the dut as needed. + //---------------------------------------------------------------- + always_ff @(posedge clk_tb) begin : sys_monitor + cycle_ctr = cycle_ctr + 1; + end + + + //---------------------------------------------------------------- + // reset_dut() + // + // Toggles reset to force the DUT into a well defined state. + //---------------------------------------------------------------- + task reset_dut; + begin + $display("*** Toggle reset."); + reset_n_tb = 0; + + repeat (2) @(posedge clk_tb); + reset_n_tb = 1; + + repeat (2) @(posedge clk_tb); + + $display(""); + end + endtask // reset_dut + + + //---------------------------------------------------------------- + // init_sim() + // + // Initialize all counters and testbed functionality as well + // as setting the DUT inputs to defined values. + //---------------------------------------------------------------- + task init_sim; + begin + generate_rng = '0; + cycle_ctr = '0; + error_ctr = '0; + tc_ctr = '0; +`ifndef VERILATOR + clk_tb = 0; +`endif + reset_n_tb = 0; + + haddr_i_tb = 0; + hwdata_i_tb = 0; + hsel_i_tb = 0; + hwrite_i_tb = 0; + hready_i_tb = 0; + htrans_i_tb = AHB_HTRANS_IDLE; + hsize_i_tb = 3'b011; + end + endtask // init_dut + + + //---------------------------------------------------------------- + // display_test_result() + // + // Display the accumulated test results. + //---------------------------------------------------------------- + task display_test_result; + begin + if (error_ctr == 0) + begin + $display("*** All %02d test cases completed successfully.", tc_ctr); + $display("* TESTCASE PASSED"); + end + else + begin + $display("*** %02d test cases completed.", tc_ctr); + $display("*** %02d errors detected during testing.", error_ctr); + $display("* TESTCASE FAILED"); + end + end + endtask // display_test_result + + //---------------------------------------------------------------- + // write_single_word() + // + // Write the given word to the DUT using the DUT interface. + //---------------------------------------------------------------- + task write_single_word( + input [spi_host_reg_pkg::BlockAw-1: 0] addr, + input [31 : 0] word + ); + + logic [31:0] address; + address = {{32-spi_host_reg_pkg::BlockAw{1'b0}}, addr}; + + $display("[%t] write_single_word(addr=0x%x, word=0x%x)", $time, address, word); + hsel_i_tb = 1; + haddr_i_tb = address; + hwdata_i_tb = word; + hwrite_i_tb = 1; + hready_i_tb = 1; + htrans_i_tb = AHB_HTRANS_NONSEQ; + hsize_i_tb = 3'b010; + + @(posedge clk_tb); + hwdata_i_tb = word; + hwrite_i_tb = 0; + htrans_i_tb = AHB_HTRANS_IDLE; + wait(hreadyout_o_tb == 1'b1); + + @(posedge clk_tb); + hsel_i_tb = 0; + endtask // write_single_word + + //---------------------------------------------------------------- + // read_word() + // + // Read a data word from the given address in the DUT. + // the word read will be available in the global variable + // read_data. + //---------------------------------------------------------------- + task read_single_word(input [spi_host_reg_pkg::BlockAw-1 : 0] addr); + logic [31:0] address; + spi_host_status_t status; + address = {{32-spi_host_reg_pkg::BlockAw{1'b0}}, addr}; + + hsel_i_tb = 1; + haddr_i_tb = address; + hwrite_i_tb = 0; + hready_i_tb = 1; + htrans_i_tb = AHB_HTRANS_NONSEQ; + hsize_i_tb = 3'b010; + + @(posedge clk_tb); + hwdata_i_tb = 0; + haddr_i_tb = 'Z; + htrans_i_tb = AHB_HTRANS_IDLE; + read_data = hrdata_o_tb; + wait(hreadyout_o_tb == 1'b1); + + @(posedge clk_tb); + hsel_i_tb = 0; + + if (addr == spi_host_reg_pkg::SPI_HOST_STATUS_OFFSET) begin + status = read_data; +`ifdef VERILATOR + $display("[%t] read_single_word(addr=0x%x) = %x {ready=%d active=%d rxqd=%d txqd=%d}", + $time, address, status, status.ready, status.active, status.rxqd, status.txqd); +`else + $display("[%t] read_single_word(addr=0x%x) = %p", $time, address, status); +`endif + end else begin + $display("[%t] read_single_word(addr=0x%x) = 0x%x", $time, address, read_data); + end + + endtask // read_word + + task set_spi_csid(input int id); + write_single_word(spi_host_reg_pkg::SPI_HOST_CSID_OFFSET, {31'h0, id[0]}); + endtask : set_spi_csid + + task spi_command ( + input int len, + input logic csaat, // chip select active after transaction + input spi_host_cmd_pkg::speed_t speed, + input spi_host_cmd_pkg::reg_direction_t direction + ); + + logic [8:0] length; + spi_host_status_t status; + length = len[8:0]; + + // Wait for Status.ready + do begin + read_single_word(spi_host_reg_pkg::SPI_HOST_STATUS_OFFSET); + status = read_data; + if (!status.ready) begin + repeat(10) @(posedge clk_tb); + end + end while (!status.ready); + + write_single_word(spi_host_reg_pkg::SPI_HOST_COMMAND_OFFSET, {18'h0, // reserved + direction, + speed, + csaat, + length}); + endtask : spi_command + + task spi_command_wait(); + spi_host_status_t status; + // Wait for Status.active + do begin + read_single_word(spi_host_reg_pkg::SPI_HOST_STATUS_OFFSET); + status = read_data; + if (status.active) begin + repeat(10) @(posedge clk_tb); + end + end while (status.active); + + endtask : spi_command_wait + + task write_tx_fifo (input logic [31:0] data); + write_single_word(spi_host_reg_pkg::SPI_HOST_TXDATA_OFFSET, data); + endtask : write_tx_fifo + + + task read_rx_fifo (output logic [31:0] data); + read_single_word(spi_host_reg_pkg::SPI_HOST_RXDATA_OFFSET); + data = read_data; + endtask : read_rx_fifo + + // configure_spi_host enables the IP and sets the protocol behavior + task configure_spi_host(input int host); + logic [spi_host_reg_pkg::BlockAw-1:0] offset; + write_single_word(spi_host_reg_pkg::SPI_HOST_CONTROL_OFFSET, {1'b1, // spi enable + 1'b0, // sw rst + 1'b1, // output enable + 13'h0, // reserved + 8'h0, // tx watermark + 8'h7f}); // rx watermark + if (host == 0) begin + offset = spi_host_reg_pkg::SPI_HOST_CONFIGOPTS_0_OFFSET; + end else begin + offset = spi_host_reg_pkg::SPI_HOST_CONFIGOPTS_1_OFFSET; + end + + write_single_word(offset, {1'b0, // Polarity + 1'b0, // Phase + 1'b0, // full cycle + 1'b0, // reserved + 4'h0, // cs_n lead time + 4'h0, // cs_n trail time + 4'h0, // minimum time between commands + 16'h0}); // core clock divider + endtask : configure_spi_host + + //---------------------------------------------------------------- + // run_jedec_id_test() + // + // Configures the spi_host to request the jedec id + // The spiflash device will return 7 bytes of continuous code ('h7f) + // followed by the JedecId ('h1f) and the DeviceId ('h1234) + //---------------------------------------------------------------- + task run_jedec_id_test(input int host); + logic [31:0] rx_data; + logic [31:0] exp_data[3]; + spi_host_status_t status; + + exp_data[0] = 32'h7f7f7f7f; + exp_data[1] = 32'h1f7f7f7f; + if (host == 0) begin + exp_data[2] = {16'h0, DeviceId0}; + end else begin + exp_data[2] = {16'h0, DeviceId1}; + end + + // Load the TX FIFO with instructions and data to be transmitted + write_tx_fifo({24'h0, CmdJedecId}); + + // Specify which device should receive the next command + set_spi_csid(host); + + // Issue speed, direction and length details for the next command + // segment. If a command consists of multiple segments, set csaat to one + // for all segments except the last one. + // + // Issue Command Instruction + spi_command(.len(0), + .csaat(1), + .speed(spi_host_cmd_pkg::Standard), + .direction(spi_host_cmd_pkg::WrOnly)); + + // spi flash will return 10 bytes for the jedec command + spi_command(.len(9), // Read Len+1 Bytes + .csaat(0), + .speed(spi_host_cmd_pkg::Standard), + .direction(spi_host_cmd_pkg::RdOnly)); + + // Wait for spi commands to finish before reading responses + spi_command_wait(); + + read_single_word(spi_host_reg_pkg::SPI_HOST_STATUS_OFFSET); + status = read_data; + // TODO: Replace with assertions when open source tooling is better + // supported. + if ({24'h0, status.rxqd} != $size(exp_data)) begin + error_ctr += 1; + $display("run_jedec_id_test: status.rxqd: Got: %d Want:%d", status.rxqd, $size(exp_data)); + end + + for (int ii = 0; ii < $size(exp_data); ii += 1) begin + read_rx_fifo(rx_data); + if (rx_data != exp_data[ii]) begin + error_ctr += 1; + $display("run_jedec_id_test: rx_data: Got: %d Want:%d", rx_data, exp_data[ii]); + end + end + endtask : run_jedec_id_test + + + //---------------------------------------------------------------- + // run_read_test() + // + // Configures the spi_host to request data from the spi flash + // The spiflash device will return data at the requested addresses + //---------------------------------------------------------------- + task run_read_test(input int host); + logic [31:0] rx_data; + logic [7:0] rx_bytes[$] = {}; + spi_host_status_t status; + logic [7:0] spi_data; + logic [19:0] spi_offset; + + localparam int NumBytes = 256; + localparam logic [23:0] SpiFlashAddr = 24'h00ABCD; + + // Load the TX FIFO with instructions and data to be transmitted + write_tx_fifo({24'h0, CmdReadQuad}); + // Upper Bytes are transmitted first, 8'h12 is ignored + write_tx_fifo({8'h12, SpiFlashAddr[7:0], + SpiFlashAddr[15:8], + SpiFlashAddr[23:16]}); + + // Specify which device should receive the next command + set_spi_csid(host); + + // Issue speed, direction and length details for the next command + // segment. If a command consists of multiple segments, set csaat to one + // for all segments except the last one. + // + // Issue Command Instruction + spi_command(.len(0), + .csaat(1), + .speed(spi_host_cmd_pkg::Standard), + .direction(spi_host_cmd_pkg::WrOnly)); + // Issue 3 Byte Address - (Send the CmdEn4B if 4B is needed) + spi_command(.len(2), + .csaat(1), + .speed(spi_host_cmd_pkg::Standard), + .direction(spi_host_cmd_pkg::WrOnly)); + + // Issue 2 Dummy Cycles - This is derived from spiflash.DummyQuad-1 + spi_command(.len(1), + .csaat(1), + .speed(spi_host_cmd_pkg::Quad), + .direction(spi_host_cmd_pkg::Dummy)); + + // Request 13 bytes of data + spi_command(.len(NumBytes-1), // Read Len+1 Bytes + .csaat(0), + .speed(spi_host_cmd_pkg::Quad), + .direction(spi_host_cmd_pkg::RdOnly)); + + // Wait for spi commands to finish before reading responses + spi_command_wait(); + + read_single_word(spi_host_reg_pkg::SPI_HOST_STATUS_OFFSET); + status = read_data; + + // Store the bytes for comparison + for (int ii = 0; ii < status.rxqd; ii += 1) begin + read_rx_fifo(rx_data); + rx_bytes.push_back(rx_data[0+:8]); + rx_bytes.push_back(rx_data[8+:8]); + rx_bytes.push_back(rx_data[16+:8]); + rx_bytes.push_back(rx_data[24+:8]); + end + + for (int ii = 0; ii < rx_bytes.size(); ii += 1) begin + if (ii >= NumBytes) begin + if (rx_bytes[ii] != 8'h0) begin + error_ctr += 1; + $display("Received Extra Non-Zero Bytes from SpiFlash. Got: %d Want: %d Size:%d", + rx_bytes[ii], 0, rx_bytes.size()); + end + continue; + end + spi_offset = SpiFlashAddr[0+:$bits(spi_offset)] + + ii[0+:$bits(spi_offset)]; + if (host == 0) begin + spi_data = spiflash0.storage[spi_offset]; + end else begin + spi_data = spiflash1.storage[spi_offset]; + end + if (rx_bytes[ii] != spi_data) begin + error_ctr += 1; + $display("[%d]spiflash.storage[%x] Got: %x Want: %x", + ii, + spi_offset, + rx_bytes[ii], + spi_data); + end + end + endtask : run_read_test + + //---------------------------------------------------------------- + // The main test functionality. + //---------------------------------------------------------------- + initial + begin : main + $display(" -- Testbench for spi_host started --"); + + init_sim(); + reset_dut(); + + for (int host = 0; host < NUM_HOSTS; host +=1) begin + configure_spi_host(host); + // Run Tests + run_jedec_id_test(host); + run_read_test(host); + run_read_test(host); + run_read_test(host); + end + + display_test_result(); + + $finish; + end // main +endmodule // spi_host_tb + +//====================================================================== +// EOF spi_host_tb.sv +//====================================================================== diff --git a/src/spi_host/tb/spiflash.sv b/src/spi_host/tb/spiflash.sv new file mode 100644 index 000000000..34338de6a --- /dev/null +++ b/src/spi_host/tb/spiflash.sv @@ -0,0 +1,518 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Behavioral model of SPI Flash +// Modifications: +// Removed all forked processes behavior since verilator doesn't support +// disable fork. This also means that the ability to detect early termination +// is removed as well. + +module spiflash #( + parameter int unsigned FlashSize = 1024*1024, // 1MB + parameter int unsigned PageSize = 256, // Bytes + + parameter bit EnFastSim = 1'b 1, // Reduce the latencies + + // Timing information s_ ms_ us + // Program Latency + parameter int unsigned tPPTyp = 1_000, // us + parameter int unsigned tPPMax = 3_000, // us + // Sector Erase(4kB) Latency + parameter int unsigned tSETyp = 45_000, // us + parameter int unsigned tSEMax = 200_000, // us + // Block Erase 32kB Latency + parameter int unsigned tBE32Typ = 200_000, // us + parameter int unsigned tBE32Max = 1_000_000, // us + // Block Erase 64kB Latency + parameter int unsigned tBE64Typ = 400_000, // us + parameter int unsigned tBE64Max = 2_000_000, // us + // Chip Erase Latency + parameter int unsigned tCETyp = 200_000_000, // us + parameter int unsigned tCEMax = 320_000_000, // us + + // Num of Repeating Continuous Code ('h7F) + parameter int unsigned CcNum = 7, + parameter logic [7:0] JedecId = 8'h 1F, + parameter logic [15:0] DeviceId = 16'h 1234, + + // Command Support + parameter bit EnFastReadDual = 1'b 1, + parameter bit EnFastReadQuad = 1'b 1, + + // Dummy Cycle + parameter int unsigned DummySizeNormal = 8, + parameter int unsigned DummySizeDual = 4, + parameter int unsigned DummySizeQuad = 2, + + // SPIFlash Default Contents - Random by default + parameter int unsigned SpiFlashRandomData = 1 +) ( + input logic sck, + input logic csb, + inout [3:0] sd + // TODO: Add WP +); + + // Local parameter : affected by EnFastSim + + // Timing information s_ ms_ us + localparam int unsigned tPPDiv = (EnFastSim) ? 1_000 : 1; + localparam int unsigned tSEDiv = (EnFastSim) ? 5_000 : 1; + localparam int unsigned tBE32Div = (EnFastSim) ? 10_000 : 1; + localparam int unsigned tBE64Div = (EnFastSim) ? 100_000 : 1; + localparam int unsigned tCEDiv = (EnFastSim) ? 10_000_000 : 1; + // Program Latency + localparam int unsigned LocalPPTyp = tPPTyp / tPPDiv; // us + localparam int unsigned LocalPPMax = tPPMax / tPPDiv; // us + // Sector Erase(4kB) Latency + localparam int unsigned LocalSETyp = tSETyp / tSEDiv; // us + localparam int unsigned LocalSEMax = tSEMax / tSEDiv; // us + // Block Erase 32kB Latency + localparam int unsigned LocalBE32Typ = tBE32Typ / tBE32Div; // us + localparam int unsigned LocalBE32Max = tBE32Max / tBE32Div; // us + // Block Erase 64kB Latency + localparam int unsigned LocalBE64Typ = tBE64Typ / tBE64Div; // us + localparam int unsigned LocalBE64Max = tBE64Max / tBE64Div; // us + // Chip Erase Latency + localparam int unsigned LocalCETyp = tCETyp / tCEDiv; // us + localparam int unsigned LocalCEMax = tCEMax / tCEDiv; // us + + + typedef enum int unsigned { + Addr3B, + Addr4B, + AddrCfg + } addr_mode_e; + + // Return Output mode + typedef enum int unsigned { + IoSingle = 1, + IoDual = 2, + IoQuad = 4 + } io_mode_e; + + typedef logic [7:0] spiflash_byte_t; + + localparam spiflash_byte_t Cc = 8'h 7F; + + localparam int unsigned StorageAw = $clog2(FlashSize); + + // Status Bit Location + typedef enum int unsigned { + StatusBUSY = 0, + StatusWEL = 1, + StatusBP0 = 2, + StatusBP1 = 3, + StatusBP2 = 4, + StatusBP3 = 5, + StatusQE = 6, // Quad Enable + StatusSRWD = 7 // Status Register Write Protect + } status_bit_e; + + // spiflash_addr_t is the smallest address width to represent FlashSize bytes + typedef logic [StorageAw-1:0] spiflash_addr_t; + + logic [3:0] sd_out, sd_en; + + assign sd[0] = sd_en[0] ? sd_out[0] : 1'b z; + assign sd[1] = sd_en[1] ? sd_out[1] : 1'b z; + assign sd[2] = sd_en[2] ? sd_out[2] : 1'b z; + assign sd[3] = sd_en[3] ? sd_out[3] : 1'b z; + + // SPI Flash internal state variables + addr_mode_e addr_mode; + spiflash_byte_t [2:0] status; // 24 bit + spiflash_byte_t storage[spiflash_addr_t]; // Associative Array to store data + + spiflash_byte_t page_buffer[PageSize]; // page buffer to be used + int unsigned page_buffer_size; + + spiflash_byte_t sfdp[256]; // SFDP storage 256Byte + + initial begin + logic [31:0] random_data; + print_banner(); + + // Init values + status = '0; + + // SFDP initialization + // FIXME: Appropriate SFDP table rather than random data + foreach(sfdp[i]) begin + random_data = $urandom_range(255, 0); + sfdp[i] = random_data[7:0]; + end + + main(); + end + + function automatic void print_banner(); + $display("/////////////////////"); + $display("// SPI Flash Model //"); + $display("/////////////////////"); + + // Print current config + $display(""); + $display("Configurations:"); + $display(" JEDEC ID: ID(0x%2X) CC_NUM (%2d) DEVICE ID: (0x%x)", JedecId, CcNum, DeviceId); + $display(" Capacity: %s", print_capacity(FlashSize)); + $display(" Fast Read: %s/ Dummy Cycle (%1d)", + "Enabled", DummySizeNormal); + $display(" Dual Mode:%s/ Dummy Cycle (%1d)", + (EnFastReadDual) ? "Enabled" : "Disabled", DummySizeDual); + $display(" Quad Mode:%s/ Dummy Cycle (%1d)", + (EnFastReadQuad) ? "Enabled" : "Disabled", DummySizeQuad); + endfunction : print_banner + + function automatic string print_capacity(int unsigned size); + string str; + // Get the highest unit (GB, MB, kB) + automatic int unsigned quotient; + automatic int unsigned order; // 0: B, 1: kB, 2: MB, 3: GB + automatic string unit; + quotient = size; + forever begin + if (quotient < 1024) break; + quotient = quotient / 1024; + order = order + 1; + end + + case (order) + 0: unit = "B"; + 1: unit = "kB"; + 2: unit = "MB"; + 3: unit = "GB"; + 4: unit = "TB"; + default: unit = "Unsupported"; + endcase + + str = $sformatf("%0d%s", quotient, unit); + return str; + endfunction : print_capacity + + task static main(); + // Main function + forever begin + automatic spiflash_byte_t opcode; + automatic int unsigned excessive_sck = 0; + // Indication of process needed after CSb release + automatic bit process_after_csb = 1'b 0; + // Big loop. Wait transaction + sd_en = 4'h 0; // Off the output by default + wait(csb == 1'b 0); + + // Main + cmdparse(opcode); // Store incoming into opcod + process_cmd(opcode, process_after_csb); + + // Float the line + sd_en = 4'h 0; + end + endtask : main + + task automatic cmdparse( + output spiflash_byte_t opcode + ); + automatic spiflash_byte_t spi_byte = 0; + get_byte(IoSingle, spi_byte); + $display("SPIFlash: Command(%2Xh) received", spi_byte); + opcode = spi_byte; + endtask : cmdparse + + task automatic process_cmd( + input spiflash_byte_t opcode, + output bit en_backend + ); + // Main case block to call subtasks depending on the opcode + en_backend = 1'b 0; + case (opcode) + spi_device_pkg::CmdReadStatus1: begin + // Return status data + return_status(0); // 0-2 + end + + spi_device_pkg::CmdJedecId: begin + return_jedec(); + end + + spi_device_pkg::CmdReadSfdp: begin + return_sfdp(); + end + + spi_device_pkg::CmdReadData, spi_device_pkg::CmdReadFast, + spi_device_pkg::CmdReadDual, spi_device_pkg::CmdReadQuad: begin + read(opcode); + end + + spi_device_pkg::CmdEn4B, spi_device_pkg::CmdEx4B: begin + addr_4b(opcode); + end + + spi_device_pkg::CmdWriteEnable, spi_device_pkg::CmdWriteDisable: begin + wel(opcode); + end + + // TODO: SectorErase + // TODO: BlockErase32 + // TODO: BlockErase64 + default: begin + $display("Unrecognized Opcode (%2Xh) received", opcode); + end + endcase + + endtask : process_cmd + + task automatic process_cmd_after_csb( + + ); + endtask : process_cmd_after_csb + + task automatic get_byte( + input io_mode_e io_mode, + output spiflash_byte_t data + ); + // Receive a byte on SPI + // Expected to be called @(negedge sck); + automatic spiflash_byte_t spi_byte = 0; + // Receive first 8 beats and decoding the byte, call subsequence tasks. + repeat(8/io_mode) begin + @(posedge sck); + case (io_mode) + IoSingle: spi_byte = {spi_byte[6:0], sd[ 0]}; + IoDual: spi_byte = {spi_byte[5:0], sd[1:0]}; + IoQuad: spi_byte = {spi_byte[3:0], sd[3:0]}; + default: spi_byte = {spi_byte[6:0], sd[ 0]}; + endcase + end + @(negedge sck); // Wait the data line fully completed + data = spi_byte; + endtask : get_byte + + task automatic get_address( + input io_mode_e imode, + input addr_mode_e amode, + output logic [31:0] addr + ); + automatic spiflash_byte_t spi_byte; + automatic addr_mode_e mode; + case (amode) + AddrCfg: mode = addr_mode; + Addr3B, Addr4B: mode = amode; + default: mode = Addr3B; + endcase + + if (mode == Addr4B) begin + // Get upper byte + get_byte(imode, spi_byte); + addr[31:24] = spi_byte; + end else begin + addr[31:24] = 8'h 0; + end + + for(int i = 2; i >= 0; i--) begin + get_byte(imode, spi_byte); + addr[8*i+:8] = spi_byte; + end + + endtask : get_address + + task automatic return_byte(spiflash_byte_t data, io_mode_e io_mode); + // Assume the task is called at @(negedge sck); + automatic int unsigned rails; + automatic logic [7:0] lines; + case (io_mode) + IoSingle: begin + rails = 1; + sd_en = 4'b 0010; + end + IoDual: begin + rails = 2; + sd_en = 4'b 0011; + end + IoQuad: begin + rails = 4; + sd_en = 4'b 1111; + end + default: begin + rails = 1; + sd_en = 4'b 0010; + end + endcase + + lines = data; + repeat(8/rails) begin + case (io_mode) + IoSingle: begin + sd_out[1] = lines[7]; + lines = {lines[6:0], 1'b 0}; + end + IoDual: begin + sd_out[1:0] = lines[7:6]; + lines = {lines[5:0], 2'b 00}; + end + IoQuad: begin + sd_out[3:0] = lines[7:4]; + lines = {lines[3:0], 4'b 0000}; + end + default: begin + sd_out[1] = lines[7]; + lines = {lines[6:0], 1'b 0}; + end + endcase + fork + begin + @(negedge sck); + end + begin + wait(csb); + end + join_any + + if (csb) begin + break; + end + end + endtask : return_byte + + task automatic dummy(int unsigned cycle); + // Assume the task is called @(negedge sck); + sd_en = 4'b 0000; + repeat (cycle) @(negedge sck); + endtask : dummy + + task automatic return_status(int unsigned idx); + // Starting from the index and wraps %3 until host releases CSb + automatic int unsigned s_idx = idx; + + // Copy the status into internal variable to not be corrupted by on-going + // backend tasks. + automatic spiflash_byte_t [2:0] committed_status = status; + forever begin + return_byte(committed_status[s_idx], IoSingle); + s_idx = (s_idx + 1)%3; + if (csb) begin + break; + end + end + endtask : return_status + + task automatic return_jedec(); + // Repeat CcNum then JedecId and DeviceId(MSB to LSB) + repeat (CcNum) begin + return_byte(Cc, IoSingle); + end + return_byte(JedecId, IoSingle); + return_byte(DeviceId[ 7:0], IoSingle); + return_byte(DeviceId[15:8], IoSingle); + // Float + endtask : return_jedec + + task automatic return_sfdp(); + automatic logic [31:0] address; + // Get address field + get_address(IoSingle, Addr3B, address); + $display("%d] SPIFlash: SFDP Address %6Xh", $time, address); + + // dummy byte + dummy(8); // 8 cycle dummy + + // fetch data and return + forever begin + return_byte(sfdp[address], IoSingle); + address += 1; + if (csb) begin + break; + end + end + endtask : return_sfdp + + task static read( + input spiflash_byte_t opcode + ); + // Return Read data + automatic logic [31:0] address; + automatic spiflash_addr_t spi_addr; + automatic io_mode_e io_mode = IoSingle; + automatic int unsigned dummy_cycle = 0; + automatic logic [31:0] random_data; + + // get address field + get_address(IoSingle, AddrCfg, address); + $display("T:%0d] SPIFlash: Read Address %8Xh", $time, address); + if (address[31:StorageAw] != '0) begin + $display("Out-of-Bound Address received: %8Xh", address); + $display(" Keep returning garbage data"); + end + + spi_addr = address[0+:StorageAw]; + + // Dummy cycle? + case (opcode) + spi_device_pkg::CmdReadData: begin + io_mode = IoSingle; + dummy_cycle = 0; + end + + spi_device_pkg::CmdReadFast: begin + io_mode = IoSingle; + dummy_cycle = DummySizeNormal; + end + + spi_device_pkg::CmdReadDual: begin + io_mode = IoDual; + dummy_cycle = DummySizeDual; + end + + spi_device_pkg::CmdReadQuad: begin + io_mode = IoQuad; + dummy_cycle = DummySizeQuad; + end + default: $display("read: Unrecognized Cmd (%2Xh)", opcode); + endcase + + if (dummy_cycle != 0) dummy(dummy_cycle); + + // fetch from storage and return + while (address <= FlashSize && !csb) begin + // Fill with random data or addr + if (SpiFlashRandomData == 1) begin + random_data = $urandom_range(255,0); + end else begin + random_data = {12'h0, spi_addr}; + end + if (storage.exists(spi_addr) == 0) storage[spi_addr] = random_data[0+:8]; + return_byte(storage[spi_addr], io_mode); + spi_addr += 1; + end + + $display("read completed"); + endtask : read + + task automatic addr_4b(spiflash_byte_t opcode); + // Set / clear addr_mode + case (opcode) + spi_device_pkg::CmdEn4B: addr_mode = Addr4B; + spi_device_pkg::CmdEx4B: addr_mode = Addr3B; + default: $display("addr_4b: Unrecognized Cmd (%2Xh)", opcode); + endcase + endtask : addr_4b + + task automatic wel(spiflash_byte_t opcode); + // TODO: Set only when CSb is deasserted right after 8th beat + case (opcode) + spi_device_pkg::CmdWriteEnable: status[0][StatusWEL] = 1'b 1; + spi_device_pkg::CmdWriteDisable: status[0][StatusWEL] = 1'b 0; + default: $display("WEL: Unrecognized Cmd (%2Xh)", opcode); + endcase + endtask : wel + + initial begin + assert(PageSize == 256) + else begin + $display("SPIFlash currently supports 256B Page only"); + $finish(); + end + end + +endmodule: spiflash diff --git a/src/spi_host/tb/test_spi_host_tb.cpp b/src/spi_host/tb/test_spi_host_tb.cpp new file mode 100644 index 000000000..da990f517 --- /dev/null +++ b/src/spi_host/tb/test_spi_host_tb.cpp @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2019 Western Digital Corporation or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include + +#include +#include +#include + +#include "Vspi_host_tb.h" +#include "verilated.h" +#include "verilated_fst_c.h" + +vluint64_t main_time = 0; + +double sc_time_stamp() { return main_time; } + +int main(int argc, char** argv) { + std::cout << "\nVerilatorTB: Start of sim\n" << std::endl; + + Verilated::commandArgs(argc, argv); + + Vspi_host_tb* tb = new Vspi_host_tb; + + // init trace dump + VerilatedFstC* tfp = NULL; + +#if VM_TRACE + Verilated::traceEverOn(true); + tfp = new VerilatedFstC; + tb->trace(tfp, 24); + tfp->open("sim.vcd"); +#endif + // Simulate + while (!Verilated::gotFinish()) { +#if VM_TRACE + tfp->dump(main_time); +#endif + main_time += 5; + tb->clk_tb = !tb->clk_tb; + tb->eval(); + } + +#if VM_TRACE + tfp->close(); +#endif + + std::cout << "\nVerilatorTB: End of sim" << std::endl; + exit(EXIT_SUCCESS); +} diff --git a/src/uart/config/uart.vf b/src/uart/config/uart.vf index 953a116f3..5f5cfae6b 100644 --- a/src/uart/config/uart.vf +++ b/src/uart/config/uart.vf @@ -24,7 +24,6 @@ ${CALIPTRA_ROOT}/src/prim/rtl/prim_pkg.sv ${CALIPTRA_ROOT}/src/lc_ctrl/rtl/lc_ctrl_reg_pkg.sv ${CALIPTRA_ROOT}/src/lc_ctrl/rtl/lc_ctrl_state_pkg.sv ${CALIPTRA_ROOT}/src/lc_ctrl/rtl/lc_ctrl_pkg.sv -${CALIPTRA_ROOT}/src/uart/rtl/uart_reg_pkg.sv ${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_flop.sv ${CALIPTRA_ROOT}/src/prim_generic/rtl/prim_generic_buf.sv ${CALIPTRA_ROOT}/src/libs/rtl/ahb_to_reg_adapter.sv @@ -58,7 +57,8 @@ ${CALIPTRA_ROOT}/src/prim/rtl/prim_sum_tree.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_subreg_ext.sv ${CALIPTRA_ROOT}/src/prim/rtl/prim_edge_detector.sv ${CALIPTRA_ROOT}/src/uart/rtl/uart_tx.sv +${CALIPTRA_ROOT}/src/uart/rtl/uart_reg_pkg.sv ${CALIPTRA_ROOT}/src/uart/rtl/uart_reg_top.sv ${CALIPTRA_ROOT}/src/uart/rtl/uart_rx.sv ${CALIPTRA_ROOT}/src/uart/rtl/uart.sv -${CALIPTRA_ROOT}/src/uart/rtl/uart_core.sv \ No newline at end of file +${CALIPTRA_ROOT}/src/uart/rtl/uart_core.sv diff --git a/src/uart/data/uart.rdl b/src/uart/data/uart.rdl new file mode 100644 index 000000000..45882690e --- /dev/null +++ b/src/uart/data/uart.rdl @@ -0,0 +1,269 @@ +addrmap uart { + reg { + field { + sw = rw; + onwrite = woclr; + desc = "raised if the transmit FIFO is past the high-water mark."; + } TX_WATERMARK[0:0]; + field { + sw = rw; + onwrite = woclr; + desc = "raised if the receive FIFO is past the high-water mark."; + } RX_WATERMARK[1:1]; + field { + sw = rw; + onwrite = woclr; + desc = "raised if the transmit FIFO has emptied and no transmit is ongoing."; + } TX_EMPTY[2:2]; + field { + sw = rw; + onwrite = woclr; + desc = "raised if the receive FIFO has overflowed."; + } RX_OVERFLOW[3:3]; + field { + sw = rw; + onwrite = woclr; + desc = "raised if a framing error has been detected on receive."; + } RX_FRAME_ERR[4:4]; + field { + sw = rw; + onwrite = woclr; + desc = "raised if break condition has been detected on receive."; + } RX_BREAK_ERR[5:5]; + field { + sw = rw; + onwrite = woclr; + desc = "raised if RX FIFO has characters remaining in the FIFO without being + retrieved for the programmed time period."; + } RX_TIMEOUT[6:6]; + field { + sw = rw; + onwrite = woclr; + desc = "raised if the receiver has detected a parity error."; + } RX_PARITY_ERR[7:7]; + } INTERRUPT_STATE @ 0x0; + reg { + field { + sw = rw; + onwrite = woclr; + desc = "Enable interrupt when tx_watermark is set."; + } TX_WATERMARK[0:0]; + field { + sw = rw; + onwrite = woclr; + desc = "Enable interrupt when rx_watermark is set."; + } RX_WATERMARK[1:1]; + field { + sw = rw; + onwrite = woclr; + desc = "Enable interrupt when tx_empty is set."; + } TX_EMPTY[2:2]; + field { + sw = rw; + onwrite = woclr; + desc = "Enable interrupt when rx_overflow is set."; + } RX_OVERFLOW[3:3]; + field { + sw = rw; + onwrite = woclr; + desc = "Enable interrupt when rx_frame_err is set."; + } RX_FRAME_ERR[4:4]; + field { + sw = rw; + onwrite = woclr; + desc = "Enable interrupt when rx_break_err is set."; + } RX_BREAK_ERR[5:5]; + field { + sw = rw; + onwrite = woclr; + desc = "Enable interrupt when rx_timeout is set."; + } RX_TIMEOUT[6:6]; + field { + sw = rw; + onwrite = woclr; + desc = "Enable interrupt when rx_parity_err is set."; + } RX_PARITY_ERR[7:7]; + } INTERRUPT_ENABLE @ 0x4; + reg { + field { + sw = w; + desc = "Write 1 to force tx_watermark to 1."; + } TX_WATERMARK[0:0]; + field { + sw = w; + desc = "Write 1 to force rx_watermark to 1."; + } RX_WATERMARK[1:1]; + field { + sw = w; + desc = "Write 1 to force tx_empty to 1."; + } TX_EMPTY[2:2]; + field { + sw = w; + desc = "Write 1 to force rx_overflow to 1."; + } RX_OVERFLOW[3:3]; + field { + sw = w; + desc = "Write 1 to force rx_frame_err to 1."; + } RX_FRAME_ERR[4:4]; + field { + sw = w; + desc = "Write 1 to force rx_break_err to 1."; + } RX_BREAK_ERR[5:5]; + field { + sw = w; + desc = "Write 1 to force rx_timeout to 1."; + } RX_TIMEOUT[6:6]; + field { + sw = w; + desc = "Write 1 to force rx_parity_err to 1."; + } RX_PARITY_ERR[7:7]; + } INTERRUPT_TEST @ 0x8; + reg { + field { + sw = w; + desc = "Write 1 to trigger one alert event of this kind."; + } FATAL_FAULT[0:0]; + } ALERT_TEST @ 0xC; + reg { + field { + desc = "TX enable"; + sw = rw; + } TX[0:0]; + field { + desc = "RX enable"; + sw = rw; + } RX[1:1]; + field { + desc = "RX noise filter enable. + If the noise filter is enabled, RX line goes through the 3-tap + repetition code. It ignores single IP clock period noise."; + sw = rw; + } NF[2:2]; + field { + desc = "System loopback enable. + + If this bit is turned on, any outgoing bits to TX are received through RX. + See Block Diagram. Note that the TX line goes 1 if System loopback is enabled."; + sw = rw; + } SLPBK[4:4]; + field { + desc = "Line loopback enable. + + If this bit is turned on, incoming bits are forwarded to TX for testing purpose. + See Block Diagram. Note that the internal design sees RX value as 1 always if line + loopback is enabled."; + sw = rw; + } LLPBK[5:5]; + field { + desc = "If true, parity is enabled in both RX and TX directions."; + sw = rw; + } PARITY_EN[6:6]; + field { + desc = "If PARITY_EN is true, this determines the type, 1 for odd parity, 0 for even."; + sw = rw; + } PARITY_ODD[7:7]; + field { + desc = "Trigger level for RX break detection. Sets the number of character + times the line must be low to detect a break."; + sw = rw; + } RXBLVL[9:8]; + field { + desc = "BAUD clock rate control."; + sw = rw; + } NCO[31:16]; + } CTRL @ 0x10; + reg { + field { + desc = "TX buffer is full"; + sw = r; + } TXFULL[0:0]; + field { + desc = "RX buffer is full"; + sw = r; + } RXFULL[1:1]; + field { + desc = "TX FIFO is empty"; + sw = r; + } TXEMPTY[2:2] = 0x1; + field { + desc = "TX FIFO is empty and all bits have been transmitted"; + sw = r; + } TXIDLE[3:3] = 0x1; + field { + desc = "RX is idle"; + sw = r; + } RXIDLE[4:4] = 0x1; + field { + desc = "RX FIFO is empty"; + sw = r; + } RXEMPTY[5:5] = 0x1; + } STATUS @ 0x14; + reg { + field { + desc = "UART read data"; + sw = r; + } RDATA[7:0]; + } RDATA @ 0x18; + reg { + field { + desc = "UART write data"; + sw = w; + } WDATA[7:0]; + } WDATA @ 0x1C; + reg { + field { + desc = "RX fifo reset. Write 1 to the register resets RX_FIFO. Read returns 0"; + sw = rw; + } RXRST[0:0]; + field { + desc = "TX fifo reset. Write 1 to the register resets TX_FIFO. Read returns 0"; + sw = rw; + } TXRST[1:1]; + field { + desc = "Trigger level for RX interrupts. If the FIFO depth is greater than or equal to + the setting, it raises rx_watermark interrupt."; + sw = rw; + } RXILVL[4:2]; + field { + desc = "Trigger level for TX interrupts. If the FIFO depth is less than the setting, it + raises tx_watermark interrupt."; + sw = rw; + } TXILVL[6:5]; + } FIFO_CTRL @ 0x20; + reg { + field { + desc = "Current fill level of TX fifo"; + sw = r; + } TXLVL[5:0]; + field { + desc = "Current fill level of RX fifo"; + sw = r; + } RXLVL[21:16]; + } FIFO_STATUS @ 0x24; + reg { + field { + desc = "Enable TX pin override control"; + sw = rw; + } TXEN[0:0]; + field { + desc = "Write to set the value of the TX pin"; + sw = rw; + } TXVAL[1:1]; + } OVRD @ 0x28; + reg { + field { + desc = "Last 16 oversampled values of RX. Most recent bit is bit 0, oldest 15."; + sw = r; + } RX[15:0]; + } VAL @ 0x2C; + reg { + field { + desc = "RX timeout value in UART bit times"; + sw = rw; + } VAL[23:0]; + field { + desc = "Enable RX timeout feature"; + sw = rw; + } EN[31:31]; + } TIMEOUT_CTRL @ 0x30; +}; \ No newline at end of file diff --git a/src/uart/tb/Makefile b/src/uart/tb/Makefile index 5763f70db..52002cfbb 100644 --- a/src/uart/tb/Makefile +++ b/src/uart/tb/Makefile @@ -44,7 +44,7 @@ verilator-build: $(TBFILES) $(VERILATOR_LINT_FILES) test_$(IPNAME)_tb.cpp $(VERILATOR) --cc \ --timing \ --timescale 1ns/1ps \ - -f $(CONFIGDIR)/$(IPNAME).vf --top-module $(IPNAME)_tb \ + -f $(CONFIGDIR)/$(IPNAME)_tb.vf --top-module $(IPNAME)_tb \ -f $(VERILATOR_LINT_FILES) \ -exe test_$(IPNAME)_tb.cpp \ --trace diff --git a/tools/scripts/Makefile b/tools/scripts/Makefile index c7d214ea6..593f204ef 100755 --- a/tools/scripts/Makefile +++ b/tools/scripts/Makefile @@ -13,8 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # + PLAYBOOK_RANDOM_SEED ?= $(shell date +%s) TEST_CFLAGS = -g -O3 -DMY_RANDOM_SEED=$(PLAYBOOK_RANDOM_SEED) + ABI = -mabi=ilp32 -march=rv32imc VERILATOR = verilator @@ -160,12 +162,13 @@ verilator-build: $(TBFILES) $(INCLUDES_DIR)/defines.h test_caliptra_top_tb.cpp $(VERILATOR) --cc -CFLAGS $(CFLAGS) \ +libext+.v+.sv +define+RV_OPENSOURCE \ --timescale 1ns/1ps \ + --timing \ $(includes) \ $(suppress) \ -f $(TBDIR)/../config/caliptra_top_tb.vf --top-module caliptra_top_tb \ -f $(TBDIR)/../config/caliptra_top_tb.vlt \ - -exe test_caliptra_top_tb.cpp --autoflush $(VERILATOR_DEBUG) - #+define+CALIPTRA_INTERNAL_TRNG + -exe test_caliptra_top_tb.cpp --autoflush $(VERILATOR_DEBUG) \ + +define+CALIPTRA_INTERNAL_QSPI+CALIPTRA_INTERNAL_TRNG+CALIPTRA_INTERNAL_UART cp $(TBDIR)/test_caliptra_top_tb.cpp obj_dir/ $(MAKE) -j -e -C obj_dir/ -f Vcaliptra_top_tb.mk $(VERILATOR_MAKE_FLAGS) VM_PARALLEL_BUILDS=1 touch verilator-build diff --git a/tools/scripts/reg_doc_gen.sh b/tools/scripts/reg_doc_gen.sh index 964c1111f..3a222c621 100755 --- a/tools/scripts/reg_doc_gen.sh +++ b/tools/scripts/reg_doc_gen.sh @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# +# # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,7 +29,9 @@ src/hmac/rtl/hmac_reg.rdl \ src/doe/rtl/doe_reg.rdl \ src/keyvault/rtl/kv_def.rdl \ src/entropy_src/data/entropy_src.rdl \ -src/csrng/data/csrng.rdl +src/csrng/data/csrng.rdl \ +src/spi_host/data/spi_host.rdl \ +src/uart/data/uart.rdl python tools/scripts/reg_doc_gen.py \ src/soc_ifc/rtl/caliptra_top_reg.rdl \ diff --git a/tools/scripts/reg_gen.py b/tools/scripts/reg_gen.py index 782055fc0..9d48a4e39 100644 --- a/tools/scripts/reg_gen.py +++ b/tools/scripts/reg_gen.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# +# # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,6 +13,21 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# This script generates systemverilog registers from rdl files +# Currently, this script uses peakrdl-regblock version 0.6.0 +# +# pip install peakrdl-regblock==0.6.0 +# +# TODO: To update this script to the latest version (0.11.0) +# 1. Import ALL_UDPS +# from peakrdl_regblock.udps import ALL_UDPS +# 2. Register ALL_UDPS after creating instance of compiler +# rdlc = RDLCompiler() +# +# Register all UDPs that 'regblock' requires +# for udp in ALL_UDPS: +# rdlc.register_udp(udp) + from systemrdl import RDLCompiler, RDLCompileError, RDLWalker from systemrdl import RDLListener from systemrdl.node import FieldNode @@ -64,6 +79,8 @@ def get_regfile_name(self): rdlc.register_udp(udp) try: + if not repo_root: + print("CALIPTRA_ROOT environment variable is not defined.") # Compile your RDL files #compile the kv defines so that rdl files including kv controls have the definition rdlc.compile_file(os.path.join(repo_root, "src/keyvault/rtl/kv_def.rdl"))