From c1d90aab84d422dbc2f4b6bc6d6f73898165a020 Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Thu, 21 May 2026 23:15:58 +0200 Subject: [PATCH 01/11] Add PeakRDL config register generation --- Bender.yml | 2 + Makefile | 3 + docs/regs/hyperbus_cfg_regs.md | 275 ++++++++ include/hyperbus_cfg_regs.h | 164 +++++ scripts/gen_regs.sh | 46 ++ src/regs/hyperbus_cfg_regblock.sv | 933 ++++++++++++++++++++++++++ src/regs/hyperbus_cfg_regblock_pkg.sv | 192 ++++++ src/regs/hyperbus_cfg_regs.rdl | 101 +++ 8 files changed, 1716 insertions(+) create mode 100644 docs/regs/hyperbus_cfg_regs.md create mode 100644 include/hyperbus_cfg_regs.h create mode 100755 scripts/gen_regs.sh create mode 100644 src/regs/hyperbus_cfg_regblock.sv create mode 100644 src/regs/hyperbus_cfg_regblock_pkg.sv create mode 100644 src/regs/hyperbus_cfg_regs.rdl diff --git a/Bender.yml b/Bender.yml index de23acc..04062ce 100644 --- a/Bender.yml +++ b/Bender.yml @@ -41,6 +41,7 @@ sources: files: - src/hyperbus_delay.sv + - src/regs/hyperbus_cfg_regblock_pkg.sv - src/hyperbus_pkg.sv - src/hyperbus_clk_gen.sv - src/hyperbus_clock_diff_out.sv @@ -48,6 +49,7 @@ sources: - src/hyperbus_phy2r.sv - src/hyperbus_ddr_out.sv - src/hyperbus_trx.sv + - src/regs/hyperbus_cfg_regblock.sv - src/hyperbus_cfg_regs.sv - src/hyperbus_phy.sv - src/hyperbus_phy_if.sv diff --git a/Makefile b/Makefile index 93b6d6f..e94ebbd 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ all: build run clean: sim_clean +update-regs: + bash scripts/gen_regs.sh + # Ensure half-built targets are purged .DELETE_ON_ERROR: diff --git a/docs/regs/hyperbus_cfg_regs.md b/docs/regs/hyperbus_cfg_regs.md new file mode 100644 index 0000000..8e995aa --- /dev/null +++ b/docs/regs/hyperbus_cfg_regs.md @@ -0,0 +1,275 @@ + + +## hyperbus_cfg_regs address map + +- Absolute Address: 0x0 +- Base Offset: 0x0 +- Size: 0x50 + +|Offset| Identifier |Name| +|------|---------------------|----| +| 0x00 | t_latency_access | — | +| 0x04 |en_latency_additional| — | +| 0x08 | t_burst_max | — | +| 0x0C |t_read_write_recovery| — | +| 0x10 | t_rx_clk_delay | — | +| 0x14 | t_tx_clk_delay | — | +| 0x18 | address_mask_msb | — | +| 0x1C | address_space | — | +| 0x20 | phys_in_use | — | +| 0x24 | which_phy | — | +| 0x28 | t_csh_cycles | — | +| 0x2C | csn_to_ck_cycles | — | +| 0x30 | chip0_base | — | +| 0x34 | chip0_bound | — | +| 0x38 | chip1_base | — | +| 0x3C | chip1_bound | — | +| 0x40 | chip2_base | — | +| 0x44 | chip2_bound | — | +| 0x48 | chip3_base | — | +| 0x4C | chip3_bound | — | + +### t_latency_access register + +- Absolute Address: 0x0 +- Base Offset: 0x0 +- Size: 0x4 + +

Initial latency cycles before read or write data.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 3:0| value | rw | 0x6 | — | + +### en_latency_additional register + +- Absolute Address: 0x4 +- Base Offset: 0x4 +- Size: 0x4 + +

Enable additional latency cycles when requested by RWDS.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 0 | value | rw | 0x0 | — | + +### t_burst_max register + +- Absolute Address: 0x8 +- Base Offset: 0x8 +- Size: 0x4 + +

Maximum continuous burst length before the PHY restarts a transfer.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +|15:0| value | rw |0x15E| — | + +### t_read_write_recovery register + +- Absolute Address: 0xC +- Base Offset: 0xC +- Size: 0x4 + +

Recovery cycles inserted between read and write phases.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 3:0| value | rw | 0x6 | — | + +### t_rx_clk_delay register + +- Absolute Address: 0x10 +- Base Offset: 0x10 +- Size: 0x4 + +

RX sampling delay-line tap setting.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| value | rw | 0x10| — | + +### t_tx_clk_delay register + +- Absolute Address: 0x14 +- Base Offset: 0x14 +- Size: 0x4 + +

TX clock delay-line tap setting.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| value | rw | 0x10| — | + +### address_mask_msb register + +- Absolute Address: 0x18 +- Base Offset: 0x18 +- Size: 0x4 + +

Most-significant address bit used by frontend address packing.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 4:0| value | rw | 0x19| — | + +### address_space register + +- Absolute Address: 0x1C +- Base Offset: 0x1C +- Size: 0x4 + +

Select HyperRAM or HyperFlash address-space interpretation.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 0 | value | rw | 0x0 | — | + +### phys_in_use register + +- Absolute Address: 0x20 +- Base Offset: 0x20 +- Size: 0x4 + +

Select whether one or both PHY lanes are active.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 0 | value | rw | 0x1 | — | + +### which_phy register + +- Absolute Address: 0x24 +- Base Offset: 0x24 +- Size: 0x4 + +

Select the PHY lane used when only one PHY is active.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 0 | value | rw | 0x1 | — | + +### t_csh_cycles register + +- Absolute Address: 0x28 +- Base Offset: 0x28 +- Size: 0x4 + +

Chip-select high time between transfers.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 3:0| value | rw | 0x1 | — | + +### csn_to_ck_cycles register + +- Absolute Address: 0x2C +- Base Offset: 0x2C +- Size: 0x4 + +

Delay cycles from chip-select assertion to clock start.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 3:0| value | rw | 0x0 | — | + +### chip0_base register + +- Absolute Address: 0x30 +- Base Offset: 0x30 +- Size: 0x4 + +

Inclusive base address for chip-select 0 decoding.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +|31:0| value | rw | 0x0 | — | + +### chip0_bound register + +- Absolute Address: 0x34 +- Base Offset: 0x34 +- Size: 0x4 + +

Exclusive bound address for chip-select 0 decoding.

+ +|Bits|Identifier|Access| Reset |Name| +|----|----------|------|-------|----| +|31:0| value | rw |0x10000| — | + +### chip1_base register + +- Absolute Address: 0x38 +- Base Offset: 0x38 +- Size: 0x4 + +

Inclusive base address for chip-select 1 decoding.

+ +|Bits|Identifier|Access| Reset |Name| +|----|----------|------|-------|----| +|31:0| value | rw |0x10000| — | + +### chip1_bound register + +- Absolute Address: 0x3C +- Base Offset: 0x3C +- Size: 0x4 + +

Exclusive bound address for chip-select 1 decoding.

+ +|Bits|Identifier|Access| Reset |Name| +|----|----------|------|-------|----| +|31:0| value | rw |0x20000| — | + +### chip2_base register + +- Absolute Address: 0x40 +- Base Offset: 0x40 +- Size: 0x4 + +

Inclusive base address for chip-select 2 decoding.

+ +|Bits|Identifier|Access| Reset |Name| +|----|----------|------|-------|----| +|31:0| value | rw |0x20000| — | + +### chip2_bound register + +- Absolute Address: 0x44 +- Base Offset: 0x44 +- Size: 0x4 + +

Exclusive bound address for chip-select 2 decoding.

+ +|Bits|Identifier|Access| Reset |Name| +|----|----------|------|-------|----| +|31:0| value | rw |0x30000| — | + +### chip3_base register + +- Absolute Address: 0x48 +- Base Offset: 0x48 +- Size: 0x4 + +

Inclusive base address for chip-select 3 decoding.

+ +|Bits|Identifier|Access| Reset |Name| +|----|----------|------|-------|----| +|31:0| value | rw |0x30000| — | + +### chip3_bound register + +- Absolute Address: 0x4C +- Base Offset: 0x4C +- Size: 0x4 + +

Exclusive bound address for chip-select 3 decoding.

+ +|Bits|Identifier|Access| Reset |Name| +|----|----------|------|-------|----| +|31:0| value | rw |0x40000| — | diff --git a/include/hyperbus_cfg_regs.h b/include/hyperbus_cfg_regs.h new file mode 100644 index 0000000..795ce1b --- /dev/null +++ b/include/hyperbus_cfg_regs.h @@ -0,0 +1,164 @@ +// Generated by PeakRDL-cheader - A free and open-source header generator +// https://github.com/SystemRDL/PeakRDL-cheader + +#ifndef HYPERBUS_CFG_REGS_H +#define HYPERBUS_CFG_REGS_H + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + +// reg - hyperbus_cfg_regs::t_latency_access +#define HYPERBUS_CFG_REGS__T_LATENCY_ACCESS__VALUE_bm 0xf +#define HYPERBUS_CFG_REGS__T_LATENCY_ACCESS__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__T_LATENCY_ACCESS__VALUE_bw 4 +#define HYPERBUS_CFG_REGS__T_LATENCY_ACCESS__VALUE_reset 0x6 + +// reg - hyperbus_cfg_regs::en_latency_additional +#define HYPERBUS_CFG_REGS__EN_LATENCY_ADDITIONAL__VALUE_bm 0x1 +#define HYPERBUS_CFG_REGS__EN_LATENCY_ADDITIONAL__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__EN_LATENCY_ADDITIONAL__VALUE_bw 1 +#define HYPERBUS_CFG_REGS__EN_LATENCY_ADDITIONAL__VALUE_reset 0x0 + +// reg - hyperbus_cfg_regs::t_burst_max +#define HYPERBUS_CFG_REGS__T_BURST_MAX__VALUE_bm 0xffff +#define HYPERBUS_CFG_REGS__T_BURST_MAX__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__T_BURST_MAX__VALUE_bw 16 +#define HYPERBUS_CFG_REGS__T_BURST_MAX__VALUE_reset 0x15e + +// reg - hyperbus_cfg_regs::t_read_write_recovery +#define HYPERBUS_CFG_REGS__T_READ_WRITE_RECOVERY__VALUE_bm 0xf +#define HYPERBUS_CFG_REGS__T_READ_WRITE_RECOVERY__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__T_READ_WRITE_RECOVERY__VALUE_bw 4 +#define HYPERBUS_CFG_REGS__T_READ_WRITE_RECOVERY__VALUE_reset 0x6 + +// reg - hyperbus_cfg_regs::t_rx_clk_delay +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__VALUE_bm 0xff +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__VALUE_bw 8 +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__VALUE_reset 0x10 + +// reg - hyperbus_cfg_regs::t_tx_clk_delay +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__VALUE_bm 0xff +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__VALUE_bw 8 +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__VALUE_reset 0x10 + +// reg - hyperbus_cfg_regs::address_mask_msb +#define HYPERBUS_CFG_REGS__ADDRESS_MASK_MSB__VALUE_bm 0x1f +#define HYPERBUS_CFG_REGS__ADDRESS_MASK_MSB__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__ADDRESS_MASK_MSB__VALUE_bw 5 +#define HYPERBUS_CFG_REGS__ADDRESS_MASK_MSB__VALUE_reset 0x19 + +// reg - hyperbus_cfg_regs::address_space +#define HYPERBUS_CFG_REGS__ADDRESS_SPACE__VALUE_bm 0x1 +#define HYPERBUS_CFG_REGS__ADDRESS_SPACE__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__ADDRESS_SPACE__VALUE_bw 1 +#define HYPERBUS_CFG_REGS__ADDRESS_SPACE__VALUE_reset 0x0 + +// reg - hyperbus_cfg_regs::phys_in_use +#define HYPERBUS_CFG_REGS__PHYS_IN_USE__VALUE_bm 0x1 +#define HYPERBUS_CFG_REGS__PHYS_IN_USE__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__PHYS_IN_USE__VALUE_bw 1 +#define HYPERBUS_CFG_REGS__PHYS_IN_USE__VALUE_reset 0x1 + +// reg - hyperbus_cfg_regs::which_phy +#define HYPERBUS_CFG_REGS__WHICH_PHY__VALUE_bm 0x1 +#define HYPERBUS_CFG_REGS__WHICH_PHY__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__WHICH_PHY__VALUE_bw 1 +#define HYPERBUS_CFG_REGS__WHICH_PHY__VALUE_reset 0x1 + +// reg - hyperbus_cfg_regs::t_csh_cycles +#define HYPERBUS_CFG_REGS__T_CSH_CYCLES__VALUE_bm 0xf +#define HYPERBUS_CFG_REGS__T_CSH_CYCLES__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__T_CSH_CYCLES__VALUE_bw 4 +#define HYPERBUS_CFG_REGS__T_CSH_CYCLES__VALUE_reset 0x1 + +// reg - hyperbus_cfg_regs::csn_to_ck_cycles +#define HYPERBUS_CFG_REGS__CSN_TO_CK_CYCLES__VALUE_bm 0xf +#define HYPERBUS_CFG_REGS__CSN_TO_CK_CYCLES__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__CSN_TO_CK_CYCLES__VALUE_bw 4 +#define HYPERBUS_CFG_REGS__CSN_TO_CK_CYCLES__VALUE_reset 0x0 + +// reg - hyperbus_cfg_regs::chip0_base +#define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_bm 0xffffffff +#define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_bw 32 +#define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_reset 0x0 + +// reg - hyperbus_cfg_regs::chip0_bound +#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_bm 0xffffffff +#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_bw 32 +#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_reset 0x10000 + +// reg - hyperbus_cfg_regs::chip1_base +#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_bm 0xffffffff +#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_bw 32 +#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_reset 0x10000 + +// reg - hyperbus_cfg_regs::chip1_bound +#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_bm 0xffffffff +#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_bw 32 +#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_reset 0x20000 + +// reg - hyperbus_cfg_regs::chip2_base +#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_bm 0xffffffff +#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_bw 32 +#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_reset 0x20000 + +// reg - hyperbus_cfg_regs::chip2_bound +#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_bm 0xffffffff +#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_bw 32 +#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_reset 0x30000 + +// reg - hyperbus_cfg_regs::chip3_base +#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_bm 0xffffffff +#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_bw 32 +#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_reset 0x30000 + +// reg - hyperbus_cfg_regs::chip3_bound +#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_bm 0xffffffff +#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_bw 32 +#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_reset 0x40000 + +// addrmap - hyperbus_cfg_regs +typedef struct __attribute__ ((__packed__)) { + uint32_t t_latency_access; + uint32_t en_latency_additional; + uint32_t t_burst_max; + uint32_t t_read_write_recovery; + uint32_t t_rx_clk_delay; + uint32_t t_tx_clk_delay; + uint32_t address_mask_msb; + uint32_t address_space; + uint32_t phys_in_use; + uint32_t which_phy; + uint32_t t_csh_cycles; + uint32_t csn_to_ck_cycles; + uint32_t chip0_base; + uint32_t chip0_bound; + uint32_t chip1_base; + uint32_t chip1_bound; + uint32_t chip2_base; + uint32_t chip2_bound; + uint32_t chip3_base; + uint32_t chip3_bound; +} hyperbus_cfg_regs_t; + + +static_assert(sizeof(hyperbus_cfg_regs_t) == 0x50, "Packing error"); + +#ifdef __cplusplus +} +#endif + +#endif /* HYPERBUS_CFG_REGS_H */ diff --git a/scripts/gen_regs.sh b/scripts/gen_regs.sh new file mode 100755 index 0000000..3d625fc --- /dev/null +++ b/scripts/gen_regs.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Copyright 2026 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 + +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +UV_CACHE_DIR="${UV_CACHE_DIR:-/tmp/uv-cache}" +UV_TOOL_DIR="${UV_TOOL_DIR:-/tmp/uv-tools}" +export UV_CACHE_DIR +export UV_TOOL_DIR + +RDL="${ROOT}/src/regs/hyperbus_cfg_regs.rdl" + +mkdir -p "${ROOT}/include" "${ROOT}/docs/regs" +rm -rf "${ROOT}/docs/regs/hyperbus_cfg_regs" + +uvx --from peakrdl-cli \ + --with peakrdl-regblock \ + --with peakrdl-cheader \ + --with peakrdl-markdown \ + peakrdl regblock \ + --cpuif apb4-flat \ + --default-reset arst_n \ + --module-name hyperbus_cfg_regblock \ + --package-name hyperbus_cfg_regblock_pkg \ + --addr-width 7 \ + -o "${ROOT}/src/regs" \ + "${RDL}" + +uvx --from peakrdl-cli \ + --with peakrdl-regblock \ + --with peakrdl-cheader \ + --with peakrdl-markdown \ + peakrdl c-header \ + -o "${ROOT}/include/hyperbus_cfg_regs.h" \ + "${RDL}" + +uvx --from peakrdl-cli \ + --with peakrdl-regblock \ + --with peakrdl-cheader \ + --with peakrdl-markdown \ + peakrdl markdown \ + -o "${ROOT}/docs/regs/hyperbus_cfg_regs.md" \ + "${RDL}" diff --git a/src/regs/hyperbus_cfg_regblock.sv b/src/regs/hyperbus_cfg_regblock.sv new file mode 100644 index 0000000..5cbcc73 --- /dev/null +++ b/src/regs/hyperbus_cfg_regblock.sv @@ -0,0 +1,933 @@ +// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator +// https://github.com/SystemRDL/PeakRDL-regblock + +module hyperbus_cfg_regblock ( + input wire clk, + input wire arst_n, + + input wire s_apb_psel, + input wire s_apb_penable, + input wire s_apb_pwrite, + input wire [2:0] s_apb_pprot, + input wire [6:0] s_apb_paddr, + input wire [31:0] s_apb_pwdata, + input wire [3:0] s_apb_pstrb, + output logic s_apb_pready, + output logic [31:0] s_apb_prdata, + output logic s_apb_pslverr, + + output hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t hwif_out + ); + + //-------------------------------------------------------------------------- + // CPU Bus interface logic + //-------------------------------------------------------------------------- + logic cpuif_req; + logic cpuif_req_is_wr; + logic [6:0] cpuif_addr; + logic [31:0] cpuif_wr_data; + logic [31:0] cpuif_wr_biten; + logic cpuif_req_stall_wr; + logic cpuif_req_stall_rd; + + logic cpuif_rd_ack; + logic cpuif_rd_err; + logic [31:0] cpuif_rd_data; + + logic cpuif_wr_ack; + logic cpuif_wr_err; + + // Request + logic is_active; + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + is_active <= '0; + cpuif_req <= '0; + cpuif_req_is_wr <= '0; + cpuif_addr <= '0; + cpuif_wr_data <= '0; + cpuif_wr_biten <= '0; + end else begin + if(~is_active) begin + if(s_apb_psel) begin + is_active <= '1; + cpuif_req <= '1; + cpuif_req_is_wr <= s_apb_pwrite; + cpuif_addr <= {s_apb_paddr[6:2], 2'b0}; + cpuif_wr_data <= s_apb_pwdata; + for(int i=0; i<4; i++) begin + cpuif_wr_biten[i*8 +: 8] <= {8{s_apb_pstrb[i]}}; + end + end + end else begin + cpuif_req <= '0; + if(cpuif_rd_ack || cpuif_wr_ack) begin + is_active <= '0; + end + end + end + end + + // Response + assign s_apb_pready = cpuif_rd_ack | cpuif_wr_ack; + assign s_apb_prdata = cpuif_rd_data; + assign s_apb_pslverr = cpuif_rd_err | cpuif_wr_err; + + logic cpuif_req_masked; + + // Read & write latencies are balanced. Stalls not required + assign cpuif_req_stall_rd = '0; + assign cpuif_req_stall_wr = '0; + assign cpuif_req_masked = cpuif_req + & !(!cpuif_req_is_wr & cpuif_req_stall_rd) + & !(cpuif_req_is_wr & cpuif_req_stall_wr); + + //-------------------------------------------------------------------------- + // Address Decode + //-------------------------------------------------------------------------- + typedef struct { + logic t_latency_access; + logic en_latency_additional; + logic t_burst_max; + logic t_read_write_recovery; + logic t_rx_clk_delay; + logic t_tx_clk_delay; + logic address_mask_msb; + logic address_space; + logic phys_in_use; + logic which_phy; + logic t_csh_cycles; + logic csn_to_ck_cycles; + logic chip0_base; + logic chip0_bound; + logic chip1_base; + logic chip1_bound; + logic chip2_base; + logic chip2_bound; + logic chip3_base; + logic chip3_bound; + } decoded_reg_strb_t; + decoded_reg_strb_t decoded_reg_strb; + logic decoded_err; + logic [6:0] decoded_addr; + logic decoded_req; + logic decoded_req_is_wr; + logic [31:0] decoded_wr_data; + logic [31:0] decoded_wr_biten; + + always_comb begin + automatic logic is_valid_addr; + automatic logic is_valid_rw; + is_valid_addr = '1; // No valid address check + is_valid_rw = '1; // No valid RW check + decoded_reg_strb.t_latency_access = cpuif_req_masked & (cpuif_addr == 7'h0); + decoded_reg_strb.en_latency_additional = cpuif_req_masked & (cpuif_addr == 7'h4); + decoded_reg_strb.t_burst_max = cpuif_req_masked & (cpuif_addr == 7'h8); + decoded_reg_strb.t_read_write_recovery = cpuif_req_masked & (cpuif_addr == 7'hc); + decoded_reg_strb.t_rx_clk_delay = cpuif_req_masked & (cpuif_addr == 7'h10); + decoded_reg_strb.t_tx_clk_delay = cpuif_req_masked & (cpuif_addr == 7'h14); + decoded_reg_strb.address_mask_msb = cpuif_req_masked & (cpuif_addr == 7'h18); + decoded_reg_strb.address_space = cpuif_req_masked & (cpuif_addr == 7'h1c); + decoded_reg_strb.phys_in_use = cpuif_req_masked & (cpuif_addr == 7'h20); + decoded_reg_strb.which_phy = cpuif_req_masked & (cpuif_addr == 7'h24); + decoded_reg_strb.t_csh_cycles = cpuif_req_masked & (cpuif_addr == 7'h28); + decoded_reg_strb.csn_to_ck_cycles = cpuif_req_masked & (cpuif_addr == 7'h2c); + decoded_reg_strb.chip0_base = cpuif_req_masked & (cpuif_addr == 7'h30); + decoded_reg_strb.chip0_bound = cpuif_req_masked & (cpuif_addr == 7'h34); + decoded_reg_strb.chip1_base = cpuif_req_masked & (cpuif_addr == 7'h38); + decoded_reg_strb.chip1_bound = cpuif_req_masked & (cpuif_addr == 7'h3c); + decoded_reg_strb.chip2_base = cpuif_req_masked & (cpuif_addr == 7'h40); + decoded_reg_strb.chip2_bound = cpuif_req_masked & (cpuif_addr == 7'h44); + decoded_reg_strb.chip3_base = cpuif_req_masked & (cpuif_addr == 7'h48); + decoded_reg_strb.chip3_bound = cpuif_req_masked & (cpuif_addr == 7'h4c); + decoded_err = '0; + end + + // Pass down signals to next stage + assign decoded_addr = cpuif_addr; + assign decoded_req = cpuif_req_masked; + assign decoded_req_is_wr = cpuif_req_is_wr; + assign decoded_wr_data = cpuif_wr_data; + assign decoded_wr_biten = cpuif_wr_biten; + + //-------------------------------------------------------------------------- + // Field logic + //-------------------------------------------------------------------------- + typedef struct { + struct { + struct { + logic [3:0] next; + logic load_next; + } value; + } t_latency_access; + struct { + struct { + logic next; + logic load_next; + } value; + } en_latency_additional; + struct { + struct { + logic [15:0] next; + logic load_next; + } value; + } t_burst_max; + struct { + struct { + logic [3:0] next; + logic load_next; + } value; + } t_read_write_recovery; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } t_rx_clk_delay; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } t_tx_clk_delay; + struct { + struct { + logic [4:0] next; + logic load_next; + } value; + } address_mask_msb; + struct { + struct { + logic next; + logic load_next; + } value; + } address_space; + struct { + struct { + logic next; + logic load_next; + } value; + } phys_in_use; + struct { + struct { + logic next; + logic load_next; + } value; + } which_phy; + struct { + struct { + logic [3:0] next; + logic load_next; + } value; + } t_csh_cycles; + struct { + struct { + logic [3:0] next; + logic load_next; + } value; + } csn_to_ck_cycles; + struct { + struct { + logic [31:0] next; + logic load_next; + } value; + } chip0_base; + struct { + struct { + logic [31:0] next; + logic load_next; + } value; + } chip0_bound; + struct { + struct { + logic [31:0] next; + logic load_next; + } value; + } chip1_base; + struct { + struct { + logic [31:0] next; + logic load_next; + } value; + } chip1_bound; + struct { + struct { + logic [31:0] next; + logic load_next; + } value; + } chip2_base; + struct { + struct { + logic [31:0] next; + logic load_next; + } value; + } chip2_bound; + struct { + struct { + logic [31:0] next; + logic load_next; + } value; + } chip3_base; + struct { + struct { + logic [31:0] next; + logic load_next; + } value; + } chip3_bound; + } field_combo_t; + field_combo_t field_combo; + + typedef struct { + struct { + struct { + logic [3:0] value; + } value; + } t_latency_access; + struct { + struct { + logic value; + } value; + } en_latency_additional; + struct { + struct { + logic [15:0] value; + } value; + } t_burst_max; + struct { + struct { + logic [3:0] value; + } value; + } t_read_write_recovery; + struct { + struct { + logic [7:0] value; + } value; + } t_rx_clk_delay; + struct { + struct { + logic [7:0] value; + } value; + } t_tx_clk_delay; + struct { + struct { + logic [4:0] value; + } value; + } address_mask_msb; + struct { + struct { + logic value; + } value; + } address_space; + struct { + struct { + logic value; + } value; + } phys_in_use; + struct { + struct { + logic value; + } value; + } which_phy; + struct { + struct { + logic [3:0] value; + } value; + } t_csh_cycles; + struct { + struct { + logic [3:0] value; + } value; + } csn_to_ck_cycles; + struct { + struct { + logic [31:0] value; + } value; + } chip0_base; + struct { + struct { + logic [31:0] value; + } value; + } chip0_bound; + struct { + struct { + logic [31:0] value; + } value; + } chip1_base; + struct { + struct { + logic [31:0] value; + } value; + } chip1_bound; + struct { + struct { + logic [31:0] value; + } value; + } chip2_base; + struct { + struct { + logic [31:0] value; + } value; + } chip2_bound; + struct { + struct { + logic [31:0] value; + } value; + } chip3_base; + struct { + struct { + logic [31:0] value; + } value; + } chip3_bound; + } field_storage_t; + field_storage_t field_storage; + + // Field: hyperbus_cfg_regs.t_latency_access.value + always_comb begin + automatic logic [3:0] next_c; + automatic logic load_next_c; + next_c = field_storage.t_latency_access.value.value; + load_next_c = '0; + if(decoded_reg_strb.t_latency_access && decoded_req_is_wr) begin // SW write + next_c = (field_storage.t_latency_access.value.value & ~decoded_wr_biten[3:0]) | (decoded_wr_data[3:0] & decoded_wr_biten[3:0]); + load_next_c = '1; + end + field_combo.t_latency_access.value.next = next_c; + field_combo.t_latency_access.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.t_latency_access.value.value <= 4'h6; + end else begin + if(field_combo.t_latency_access.value.load_next) begin + field_storage.t_latency_access.value.value <= field_combo.t_latency_access.value.next; + end + end + end + assign hwif_out.t_latency_access.value.value = field_storage.t_latency_access.value.value; + // Field: hyperbus_cfg_regs.en_latency_additional.value + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.en_latency_additional.value.value; + load_next_c = '0; + if(decoded_reg_strb.en_latency_additional && decoded_req_is_wr) begin // SW write + next_c = (field_storage.en_latency_additional.value.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]); + load_next_c = '1; + end + field_combo.en_latency_additional.value.next = next_c; + field_combo.en_latency_additional.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.en_latency_additional.value.value <= 1'h0; + end else begin + if(field_combo.en_latency_additional.value.load_next) begin + field_storage.en_latency_additional.value.value <= field_combo.en_latency_additional.value.next; + end + end + end + assign hwif_out.en_latency_additional.value.value = field_storage.en_latency_additional.value.value; + // Field: hyperbus_cfg_regs.t_burst_max.value + always_comb begin + automatic logic [15:0] next_c; + automatic logic load_next_c; + next_c = field_storage.t_burst_max.value.value; + load_next_c = '0; + if(decoded_reg_strb.t_burst_max && decoded_req_is_wr) begin // SW write + next_c = (field_storage.t_burst_max.value.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]); + load_next_c = '1; + end + field_combo.t_burst_max.value.next = next_c; + field_combo.t_burst_max.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.t_burst_max.value.value <= 16'h15e; + end else begin + if(field_combo.t_burst_max.value.load_next) begin + field_storage.t_burst_max.value.value <= field_combo.t_burst_max.value.next; + end + end + end + assign hwif_out.t_burst_max.value.value = field_storage.t_burst_max.value.value; + // Field: hyperbus_cfg_regs.t_read_write_recovery.value + always_comb begin + automatic logic [3:0] next_c; + automatic logic load_next_c; + next_c = field_storage.t_read_write_recovery.value.value; + load_next_c = '0; + if(decoded_reg_strb.t_read_write_recovery && decoded_req_is_wr) begin // SW write + next_c = (field_storage.t_read_write_recovery.value.value & ~decoded_wr_biten[3:0]) | (decoded_wr_data[3:0] & decoded_wr_biten[3:0]); + load_next_c = '1; + end + field_combo.t_read_write_recovery.value.next = next_c; + field_combo.t_read_write_recovery.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.t_read_write_recovery.value.value <= 4'h6; + end else begin + if(field_combo.t_read_write_recovery.value.load_next) begin + field_storage.t_read_write_recovery.value.value <= field_combo.t_read_write_recovery.value.next; + end + end + end + assign hwif_out.t_read_write_recovery.value.value = field_storage.t_read_write_recovery.value.value; + // Field: hyperbus_cfg_regs.t_rx_clk_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.t_rx_clk_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.t_rx_clk_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.t_rx_clk_delay.value.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.t_rx_clk_delay.value.next = next_c; + field_combo.t_rx_clk_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.t_rx_clk_delay.value.value <= 8'h10; + end else begin + if(field_combo.t_rx_clk_delay.value.load_next) begin + field_storage.t_rx_clk_delay.value.value <= field_combo.t_rx_clk_delay.value.next; + end + end + end + assign hwif_out.t_rx_clk_delay.value.value = field_storage.t_rx_clk_delay.value.value; + // Field: hyperbus_cfg_regs.t_tx_clk_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.t_tx_clk_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.t_tx_clk_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.t_tx_clk_delay.value.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.t_tx_clk_delay.value.next = next_c; + field_combo.t_tx_clk_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.t_tx_clk_delay.value.value <= 8'h10; + end else begin + if(field_combo.t_tx_clk_delay.value.load_next) begin + field_storage.t_tx_clk_delay.value.value <= field_combo.t_tx_clk_delay.value.next; + end + end + end + assign hwif_out.t_tx_clk_delay.value.value = field_storage.t_tx_clk_delay.value.value; + // Field: hyperbus_cfg_regs.address_mask_msb.value + always_comb begin + automatic logic [4:0] next_c; + automatic logic load_next_c; + next_c = field_storage.address_mask_msb.value.value; + load_next_c = '0; + if(decoded_reg_strb.address_mask_msb && decoded_req_is_wr) begin // SW write + next_c = (field_storage.address_mask_msb.value.value & ~decoded_wr_biten[4:0]) | (decoded_wr_data[4:0] & decoded_wr_biten[4:0]); + load_next_c = '1; + end + field_combo.address_mask_msb.value.next = next_c; + field_combo.address_mask_msb.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.address_mask_msb.value.value <= 5'h19; + end else begin + if(field_combo.address_mask_msb.value.load_next) begin + field_storage.address_mask_msb.value.value <= field_combo.address_mask_msb.value.next; + end + end + end + assign hwif_out.address_mask_msb.value.value = field_storage.address_mask_msb.value.value; + // Field: hyperbus_cfg_regs.address_space.value + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.address_space.value.value; + load_next_c = '0; + if(decoded_reg_strb.address_space && decoded_req_is_wr) begin // SW write + next_c = (field_storage.address_space.value.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]); + load_next_c = '1; + end + field_combo.address_space.value.next = next_c; + field_combo.address_space.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.address_space.value.value <= 1'h0; + end else begin + if(field_combo.address_space.value.load_next) begin + field_storage.address_space.value.value <= field_combo.address_space.value.next; + end + end + end + assign hwif_out.address_space.value.value = field_storage.address_space.value.value; + // Field: hyperbus_cfg_regs.phys_in_use.value + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.phys_in_use.value.value; + load_next_c = '0; + if(decoded_reg_strb.phys_in_use && decoded_req_is_wr) begin // SW write + next_c = (field_storage.phys_in_use.value.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]); + load_next_c = '1; + end + field_combo.phys_in_use.value.next = next_c; + field_combo.phys_in_use.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.phys_in_use.value.value <= 1'h1; + end else begin + if(field_combo.phys_in_use.value.load_next) begin + field_storage.phys_in_use.value.value <= field_combo.phys_in_use.value.next; + end + end + end + assign hwif_out.phys_in_use.value.value = field_storage.phys_in_use.value.value; + // Field: hyperbus_cfg_regs.which_phy.value + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.which_phy.value.value; + load_next_c = '0; + if(decoded_reg_strb.which_phy && decoded_req_is_wr) begin // SW write + next_c = (field_storage.which_phy.value.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]); + load_next_c = '1; + end + field_combo.which_phy.value.next = next_c; + field_combo.which_phy.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.which_phy.value.value <= 1'h1; + end else begin + if(field_combo.which_phy.value.load_next) begin + field_storage.which_phy.value.value <= field_combo.which_phy.value.next; + end + end + end + assign hwif_out.which_phy.value.value = field_storage.which_phy.value.value; + // Field: hyperbus_cfg_regs.t_csh_cycles.value + always_comb begin + automatic logic [3:0] next_c; + automatic logic load_next_c; + next_c = field_storage.t_csh_cycles.value.value; + load_next_c = '0; + if(decoded_reg_strb.t_csh_cycles && decoded_req_is_wr) begin // SW write + next_c = (field_storage.t_csh_cycles.value.value & ~decoded_wr_biten[3:0]) | (decoded_wr_data[3:0] & decoded_wr_biten[3:0]); + load_next_c = '1; + end + field_combo.t_csh_cycles.value.next = next_c; + field_combo.t_csh_cycles.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.t_csh_cycles.value.value <= 4'h1; + end else begin + if(field_combo.t_csh_cycles.value.load_next) begin + field_storage.t_csh_cycles.value.value <= field_combo.t_csh_cycles.value.next; + end + end + end + assign hwif_out.t_csh_cycles.value.value = field_storage.t_csh_cycles.value.value; + // Field: hyperbus_cfg_regs.csn_to_ck_cycles.value + always_comb begin + automatic logic [3:0] next_c; + automatic logic load_next_c; + next_c = field_storage.csn_to_ck_cycles.value.value; + load_next_c = '0; + if(decoded_reg_strb.csn_to_ck_cycles && decoded_req_is_wr) begin // SW write + next_c = (field_storage.csn_to_ck_cycles.value.value & ~decoded_wr_biten[3:0]) | (decoded_wr_data[3:0] & decoded_wr_biten[3:0]); + load_next_c = '1; + end + field_combo.csn_to_ck_cycles.value.next = next_c; + field_combo.csn_to_ck_cycles.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.csn_to_ck_cycles.value.value <= 4'h0; + end else begin + if(field_combo.csn_to_ck_cycles.value.load_next) begin + field_storage.csn_to_ck_cycles.value.value <= field_combo.csn_to_ck_cycles.value.next; + end + end + end + assign hwif_out.csn_to_ck_cycles.value.value = field_storage.csn_to_ck_cycles.value.value; + // Field: hyperbus_cfg_regs.chip0_base.value + always_comb begin + automatic logic [31:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip0_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip0_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip0_base.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + load_next_c = '1; + end + field_combo.chip0_base.value.next = next_c; + field_combo.chip0_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip0_base.value.value <= 32'h0; + end else begin + if(field_combo.chip0_base.value.load_next) begin + field_storage.chip0_base.value.value <= field_combo.chip0_base.value.next; + end + end + end + assign hwif_out.chip0_base.value.value = field_storage.chip0_base.value.value; + // Field: hyperbus_cfg_regs.chip0_bound.value + always_comb begin + automatic logic [31:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip0_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip0_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip0_bound.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + load_next_c = '1; + end + field_combo.chip0_bound.value.next = next_c; + field_combo.chip0_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip0_bound.value.value <= 32'h10000; + end else begin + if(field_combo.chip0_bound.value.load_next) begin + field_storage.chip0_bound.value.value <= field_combo.chip0_bound.value.next; + end + end + end + assign hwif_out.chip0_bound.value.value = field_storage.chip0_bound.value.value; + // Field: hyperbus_cfg_regs.chip1_base.value + always_comb begin + automatic logic [31:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip1_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip1_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip1_base.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + load_next_c = '1; + end + field_combo.chip1_base.value.next = next_c; + field_combo.chip1_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip1_base.value.value <= 32'h10000; + end else begin + if(field_combo.chip1_base.value.load_next) begin + field_storage.chip1_base.value.value <= field_combo.chip1_base.value.next; + end + end + end + assign hwif_out.chip1_base.value.value = field_storage.chip1_base.value.value; + // Field: hyperbus_cfg_regs.chip1_bound.value + always_comb begin + automatic logic [31:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip1_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip1_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip1_bound.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + load_next_c = '1; + end + field_combo.chip1_bound.value.next = next_c; + field_combo.chip1_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip1_bound.value.value <= 32'h20000; + end else begin + if(field_combo.chip1_bound.value.load_next) begin + field_storage.chip1_bound.value.value <= field_combo.chip1_bound.value.next; + end + end + end + assign hwif_out.chip1_bound.value.value = field_storage.chip1_bound.value.value; + // Field: hyperbus_cfg_regs.chip2_base.value + always_comb begin + automatic logic [31:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip2_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip2_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip2_base.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + load_next_c = '1; + end + field_combo.chip2_base.value.next = next_c; + field_combo.chip2_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip2_base.value.value <= 32'h20000; + end else begin + if(field_combo.chip2_base.value.load_next) begin + field_storage.chip2_base.value.value <= field_combo.chip2_base.value.next; + end + end + end + assign hwif_out.chip2_base.value.value = field_storage.chip2_base.value.value; + // Field: hyperbus_cfg_regs.chip2_bound.value + always_comb begin + automatic logic [31:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip2_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip2_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip2_bound.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + load_next_c = '1; + end + field_combo.chip2_bound.value.next = next_c; + field_combo.chip2_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip2_bound.value.value <= 32'h30000; + end else begin + if(field_combo.chip2_bound.value.load_next) begin + field_storage.chip2_bound.value.value <= field_combo.chip2_bound.value.next; + end + end + end + assign hwif_out.chip2_bound.value.value = field_storage.chip2_bound.value.value; + // Field: hyperbus_cfg_regs.chip3_base.value + always_comb begin + automatic logic [31:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip3_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip3_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip3_base.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + load_next_c = '1; + end + field_combo.chip3_base.value.next = next_c; + field_combo.chip3_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip3_base.value.value <= 32'h30000; + end else begin + if(field_combo.chip3_base.value.load_next) begin + field_storage.chip3_base.value.value <= field_combo.chip3_base.value.next; + end + end + end + assign hwif_out.chip3_base.value.value = field_storage.chip3_base.value.value; + // Field: hyperbus_cfg_regs.chip3_bound.value + always_comb begin + automatic logic [31:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip3_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip3_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip3_bound.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + load_next_c = '1; + end + field_combo.chip3_bound.value.next = next_c; + field_combo.chip3_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip3_bound.value.value <= 32'h40000; + end else begin + if(field_combo.chip3_bound.value.load_next) begin + field_storage.chip3_bound.value.value <= field_combo.chip3_bound.value.next; + end + end + end + assign hwif_out.chip3_bound.value.value = field_storage.chip3_bound.value.value; + + //-------------------------------------------------------------------------- + // Write response + //-------------------------------------------------------------------------- + assign cpuif_wr_ack = decoded_req & decoded_req_is_wr; + // Writes are always granted with no error response + assign cpuif_wr_err = '0; + + //-------------------------------------------------------------------------- + // Readback + //-------------------------------------------------------------------------- + + logic [6:0] rd_mux_addr; + assign rd_mux_addr = decoded_addr; + + logic readback_err; + logic readback_done; + logic [31:0] readback_data; + always_comb begin + automatic logic [31:0] readback_data_var; + readback_data_var = '0; + if(rd_mux_addr == 7'h0) begin + readback_data_var[3:0] = field_storage.t_latency_access.value.value; + end + if(rd_mux_addr == 7'h4) begin + readback_data_var[0] = field_storage.en_latency_additional.value.value; + end + if(rd_mux_addr == 7'h8) begin + readback_data_var[15:0] = field_storage.t_burst_max.value.value; + end + if(rd_mux_addr == 7'hc) begin + readback_data_var[3:0] = field_storage.t_read_write_recovery.value.value; + end + if(rd_mux_addr == 7'h10) begin + readback_data_var[7:0] = field_storage.t_rx_clk_delay.value.value; + end + if(rd_mux_addr == 7'h14) begin + readback_data_var[7:0] = field_storage.t_tx_clk_delay.value.value; + end + if(rd_mux_addr == 7'h18) begin + readback_data_var[4:0] = field_storage.address_mask_msb.value.value; + end + if(rd_mux_addr == 7'h1c) begin + readback_data_var[0] = field_storage.address_space.value.value; + end + if(rd_mux_addr == 7'h20) begin + readback_data_var[0] = field_storage.phys_in_use.value.value; + end + if(rd_mux_addr == 7'h24) begin + readback_data_var[0] = field_storage.which_phy.value.value; + end + if(rd_mux_addr == 7'h28) begin + readback_data_var[3:0] = field_storage.t_csh_cycles.value.value; + end + if(rd_mux_addr == 7'h2c) begin + readback_data_var[3:0] = field_storage.csn_to_ck_cycles.value.value; + end + if(rd_mux_addr == 7'h30) begin + readback_data_var[31:0] = field_storage.chip0_base.value.value; + end + if(rd_mux_addr == 7'h34) begin + readback_data_var[31:0] = field_storage.chip0_bound.value.value; + end + if(rd_mux_addr == 7'h38) begin + readback_data_var[31:0] = field_storage.chip1_base.value.value; + end + if(rd_mux_addr == 7'h3c) begin + readback_data_var[31:0] = field_storage.chip1_bound.value.value; + end + if(rd_mux_addr == 7'h40) begin + readback_data_var[31:0] = field_storage.chip2_base.value.value; + end + if(rd_mux_addr == 7'h44) begin + readback_data_var[31:0] = field_storage.chip2_bound.value.value; + end + if(rd_mux_addr == 7'h48) begin + readback_data_var[31:0] = field_storage.chip3_base.value.value; + end + if(rd_mux_addr == 7'h4c) begin + readback_data_var[31:0] = field_storage.chip3_bound.value.value; + end + readback_data = readback_data_var; + readback_done = decoded_req & ~decoded_req_is_wr; + readback_err = '0; + end + + assign cpuif_rd_ack = readback_done; + assign cpuif_rd_data = readback_data; + assign cpuif_rd_err = readback_err; +endmodule diff --git a/src/regs/hyperbus_cfg_regblock_pkg.sv b/src/regs/hyperbus_cfg_regblock_pkg.sv new file mode 100644 index 0000000..59572c7 --- /dev/null +++ b/src/regs/hyperbus_cfg_regblock_pkg.sv @@ -0,0 +1,192 @@ +// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator +// https://github.com/SystemRDL/PeakRDL-regblock + +package hyperbus_cfg_regblock_pkg; + + localparam HYPERBUS_CFG_REGBLOCK_DATA_WIDTH = 32; + localparam HYPERBUS_CFG_REGBLOCK_MIN_ADDR_WIDTH = 7; + localparam HYPERBUS_CFG_REGBLOCK_SIZE = 'h50; + + typedef struct { + logic [3:0] value; + } hyperbus_cfg_regs__t_latency_access__value__out_t; + + typedef struct { + hyperbus_cfg_regs__t_latency_access__value__out_t value; + } hyperbus_cfg_regs__t_latency_access__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__en_latency_additional__value__out_t; + + typedef struct { + hyperbus_cfg_regs__en_latency_additional__value__out_t value; + } hyperbus_cfg_regs__en_latency_additional__out_t; + + typedef struct { + logic [15:0] value; + } hyperbus_cfg_regs__t_burst_max__value__out_t; + + typedef struct { + hyperbus_cfg_regs__t_burst_max__value__out_t value; + } hyperbus_cfg_regs__t_burst_max__out_t; + + typedef struct { + logic [3:0] value; + } hyperbus_cfg_regs__t_read_write_recovery__value__out_t; + + typedef struct { + hyperbus_cfg_regs__t_read_write_recovery__value__out_t value; + } hyperbus_cfg_regs__t_read_write_recovery__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__t_rx_clk_delay__value__out_t; + + typedef struct { + hyperbus_cfg_regs__t_rx_clk_delay__value__out_t value; + } hyperbus_cfg_regs__t_rx_clk_delay__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__t_tx_clk_delay__value__out_t; + + typedef struct { + hyperbus_cfg_regs__t_tx_clk_delay__value__out_t value; + } hyperbus_cfg_regs__t_tx_clk_delay__out_t; + + typedef struct { + logic [4:0] value; + } hyperbus_cfg_regs__address_mask_msb__value__out_t; + + typedef struct { + hyperbus_cfg_regs__address_mask_msb__value__out_t value; + } hyperbus_cfg_regs__address_mask_msb__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__address_space__value__out_t; + + typedef struct { + hyperbus_cfg_regs__address_space__value__out_t value; + } hyperbus_cfg_regs__address_space__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__phys_in_use__value__out_t; + + typedef struct { + hyperbus_cfg_regs__phys_in_use__value__out_t value; + } hyperbus_cfg_regs__phys_in_use__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__which_phy__value__out_t; + + typedef struct { + hyperbus_cfg_regs__which_phy__value__out_t value; + } hyperbus_cfg_regs__which_phy__out_t; + + typedef struct { + logic [3:0] value; + } hyperbus_cfg_regs__t_csh_cycles__value__out_t; + + typedef struct { + hyperbus_cfg_regs__t_csh_cycles__value__out_t value; + } hyperbus_cfg_regs__t_csh_cycles__out_t; + + typedef struct { + logic [3:0] value; + } hyperbus_cfg_regs__csn_to_ck_cycles__value__out_t; + + typedef struct { + hyperbus_cfg_regs__csn_to_ck_cycles__value__out_t value; + } hyperbus_cfg_regs__csn_to_ck_cycles__out_t; + + typedef struct { + logic [31:0] value; + } hyperbus_cfg_regs__chip0_base__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip0_base__value__out_t value; + } hyperbus_cfg_regs__chip0_base__out_t; + + typedef struct { + logic [31:0] value; + } hyperbus_cfg_regs__chip0_bound__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip0_bound__value__out_t value; + } hyperbus_cfg_regs__chip0_bound__out_t; + + typedef struct { + logic [31:0] value; + } hyperbus_cfg_regs__chip1_base__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip1_base__value__out_t value; + } hyperbus_cfg_regs__chip1_base__out_t; + + typedef struct { + logic [31:0] value; + } hyperbus_cfg_regs__chip1_bound__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip1_bound__value__out_t value; + } hyperbus_cfg_regs__chip1_bound__out_t; + + typedef struct { + logic [31:0] value; + } hyperbus_cfg_regs__chip2_base__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip2_base__value__out_t value; + } hyperbus_cfg_regs__chip2_base__out_t; + + typedef struct { + logic [31:0] value; + } hyperbus_cfg_regs__chip2_bound__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip2_bound__value__out_t value; + } hyperbus_cfg_regs__chip2_bound__out_t; + + typedef struct { + logic [31:0] value; + } hyperbus_cfg_regs__chip3_base__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip3_base__value__out_t value; + } hyperbus_cfg_regs__chip3_base__out_t; + + typedef struct { + logic [31:0] value; + } hyperbus_cfg_regs__chip3_bound__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip3_bound__value__out_t value; + } hyperbus_cfg_regs__chip3_bound__out_t; + + typedef struct { + hyperbus_cfg_regs__t_latency_access__out_t t_latency_access; + hyperbus_cfg_regs__en_latency_additional__out_t en_latency_additional; + hyperbus_cfg_regs__t_burst_max__out_t t_burst_max; + hyperbus_cfg_regs__t_read_write_recovery__out_t t_read_write_recovery; + hyperbus_cfg_regs__t_rx_clk_delay__out_t t_rx_clk_delay; + hyperbus_cfg_regs__t_tx_clk_delay__out_t t_tx_clk_delay; + hyperbus_cfg_regs__address_mask_msb__out_t address_mask_msb; + hyperbus_cfg_regs__address_space__out_t address_space; + hyperbus_cfg_regs__phys_in_use__out_t phys_in_use; + hyperbus_cfg_regs__which_phy__out_t which_phy; + hyperbus_cfg_regs__t_csh_cycles__out_t t_csh_cycles; + hyperbus_cfg_regs__csn_to_ck_cycles__out_t csn_to_ck_cycles; + hyperbus_cfg_regs__chip0_base__out_t chip0_base; + hyperbus_cfg_regs__chip0_bound__out_t chip0_bound; + hyperbus_cfg_regs__chip1_base__out_t chip1_base; + hyperbus_cfg_regs__chip1_bound__out_t chip1_bound; + hyperbus_cfg_regs__chip2_base__out_t chip2_base; + hyperbus_cfg_regs__chip2_bound__out_t chip2_bound; + hyperbus_cfg_regs__chip3_base__out_t chip3_base; + hyperbus_cfg_regs__chip3_bound__out_t chip3_bound; + } hyperbus_cfg_regs__out_t; +endpackage diff --git a/src/regs/hyperbus_cfg_regs.rdl b/src/regs/hyperbus_cfg_regs.rdl new file mode 100644 index 0000000..5c27572 --- /dev/null +++ b/src/regs/hyperbus_cfg_regs.rdl @@ -0,0 +1,101 @@ +addrmap hyperbus_cfg_regs { + reg { + desc = "Initial latency cycles before read or write data."; + field { sw = rw; hw = r; reset = 4'h6; } value[3:0]; + } t_latency_access @ 0x00; + + reg { + desc = "Enable additional latency cycles when requested by RWDS."; + field { sw = rw; hw = r; reset = 1'b0; } value; + } en_latency_additional @ 0x04; + + reg { + desc = "Maximum continuous burst length before the PHY restarts a transfer."; + field { sw = rw; hw = r; reset = 16'd350; } value[15:0]; + } t_burst_max @ 0x08; + + reg { + desc = "Recovery cycles inserted between read and write phases."; + field { sw = rw; hw = r; reset = 4'h6; } value[3:0]; + } t_read_write_recovery @ 0x0c; + + reg { + desc = "RX sampling delay-line tap setting."; + field { sw = rw; hw = r; reset = 8'h10; } value[7:0]; + } t_rx_clk_delay @ 0x10; + + reg { + desc = "TX clock delay-line tap setting."; + field { sw = rw; hw = r; reset = 8'h10; } value[7:0]; + } t_tx_clk_delay @ 0x14; + + reg { + desc = "Most-significant address bit used by frontend address packing."; + field { sw = rw; hw = r; reset = 5'd25; } value[4:0]; + } address_mask_msb @ 0x18; + + reg { + desc = "Select HyperRAM or HyperFlash address-space interpretation."; + field { sw = rw; hw = r; reset = 1'b0; } value; + } address_space @ 0x1c; + + reg { + desc = "Select whether one or both PHY lanes are active."; + field { sw = rw; hw = r; reset = 1'b1; } value; + } phys_in_use @ 0x20; + + reg { + desc = "Select the PHY lane used when only one PHY is active."; + field { sw = rw; hw = r; reset = 1'b1; } value; + } which_phy @ 0x24; + + reg { + desc = "Chip-select high time between transfers."; + field { sw = rw; hw = r; reset = 4'h1; } value[3:0]; + } t_csh_cycles @ 0x28; + + reg { + desc = "Delay cycles from chip-select assertion to clock start."; + field { sw = rw; hw = r; reset = 4'h0; } value[3:0]; + } csn_to_ck_cycles @ 0x2c; + + reg { + desc = "Inclusive base address for chip-select 0 decoding."; + field { sw = rw; hw = r; reset = 32'h0000_0000; } value[31:0]; + } chip0_base @ 0x30; + + reg { + desc = "Exclusive bound address for chip-select 0 decoding."; + field { sw = rw; hw = r; reset = 32'h0001_0000; } value[31:0]; + } chip0_bound @ 0x34; + + reg { + desc = "Inclusive base address for chip-select 1 decoding."; + field { sw = rw; hw = r; reset = 32'h0001_0000; } value[31:0]; + } chip1_base @ 0x38; + + reg { + desc = "Exclusive bound address for chip-select 1 decoding."; + field { sw = rw; hw = r; reset = 32'h0002_0000; } value[31:0]; + } chip1_bound @ 0x3c; + + reg { + desc = "Inclusive base address for chip-select 2 decoding."; + field { sw = rw; hw = r; reset = 32'h0002_0000; } value[31:0]; + } chip2_base @ 0x40; + + reg { + desc = "Exclusive bound address for chip-select 2 decoding."; + field { sw = rw; hw = r; reset = 32'h0003_0000; } value[31:0]; + } chip2_bound @ 0x44; + + reg { + desc = "Inclusive base address for chip-select 3 decoding."; + field { sw = rw; hw = r; reset = 32'h0003_0000; } value[31:0]; + } chip3_base @ 0x48; + + reg { + desc = "Exclusive bound address for chip-select 3 decoding."; + field { sw = rw; hw = r; reset = 32'h0004_0000; } value[31:0]; + } chip3_bound @ 0x4c; +}; From 4b73a5c5ff642911a1efe9fe102836585c137a9f Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Thu, 21 May 2026 23:16:18 +0200 Subject: [PATCH 02/11] Use generated HyperBus config registers --- src/hyperbus.sv | 28 ++--- src/hyperbus_axi.sv | 27 ++--- src/hyperbus_cfg_regs.sv | 242 ++++++++++++++++++++++--------------- src/hyperbus_phy.sv | 32 ++--- src/hyperbus_phy_if.sv | 2 +- src/hyperbus_pkg.sv | 75 +++++++----- src/hyperbus_synth_wrap.sv | 1 - test/dut_if.sv | 1 - test/fixture_hyperbus.sv | 1 - 9 files changed, 231 insertions(+), 178 deletions(-) diff --git a/src/hyperbus.sv b/src/hyperbus.sv index da287fa..ff0faa6 100644 --- a/src/hyperbus.sv +++ b/src/hyperbus.sv @@ -21,18 +21,13 @@ module hyperbus #( parameter type axi_ar_chan_t = logic, parameter type axi_r_chan_t = logic, parameter type axi_aw_chan_t = logic, - parameter int unsigned RegAddrWidth = -1, parameter int unsigned RegDataWidth = -1, - parameter int unsigned MinFreqMHz = 100, parameter type reg_req_t = logic, parameter type reg_rsp_t = logic, parameter type axi_rule_t = logic, // The below have sensible defaults, but should be set on integration! parameter int unsigned RxFifoLogDepth = 3, parameter int unsigned TxFifoLogDepth = 3, - parameter logic [RegDataWidth-1:0] RstChipBase = 'h0, // Base address for all chips - parameter logic [RegDataWidth-1:0] RstChipSpace = 'h1_0000, // 64 KiB: Current maximum HyperBus device size - parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys,MinFreqMHz), parameter int unsigned PhyStartupCycles = 300 * 200, /* us*MHz */ // Conservative maximum frequency estimate parameter int unsigned SyncStages = 2 ) ( @@ -85,9 +80,10 @@ module hyperbus #( logic clk_phy_0, clk_phy_90, rst_phy; // Register file - hyperbus_pkg::hyper_cfg_t cfg; - axi_rule_t [NumChips-1:0] chip_rules; - logic trans_active; + hyperbus_pkg::frontend_cfg_t frontend_cfg; + hyperbus_pkg::phy_cfg_t phy_cfg; + axi_rule_t [NumChips-1:0] chip_rules; + logic trans_active; // AXI slave hyper_rx_t axi_rx; @@ -121,20 +117,17 @@ module hyperbus #( hyperbus_cfg_regs #( .NumChips ( NumChips ), .NumPhys ( NumPhys ), - .RegAddrWidth ( RegAddrWidth ), .RegDataWidth ( RegDataWidth ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), - .rule_t ( axi_rule_t ), - .RstChipBase ( RstChipBase ), - .RstChipSpace ( RstChipSpace ), - .RstCfg ( RstCfg ) + .rule_t ( axi_rule_t ) ) i_cfg_regs ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), .reg_req_i ( reg_req_i ), .reg_rsp_o ( reg_rsp_o ), - .cfg_o ( cfg ), + .frontend_cfg_o ( frontend_cfg ), + .phy_cfg_o ( phy_cfg ), .chip_rules_o ( chip_rules ), .trans_active_i ( trans_active ) ); @@ -173,11 +166,8 @@ module hyperbus #( .trans_valid_o ( axi_trans_valid ), .trans_ready_i ( axi_trans_ready ), + .frontend_cfg_i ( frontend_cfg ), .chip_rules_i ( chip_rules ), - .which_phy_i ( cfg.which_phy ), - .phys_in_use_i ( cfg.phys_in_use ), - .addr_mask_msb_i ( cfg.address_mask_msb ), - .addr_space_i ( cfg.address_space ), .trans_active_o ( trans_active ) ); @@ -214,7 +204,7 @@ module hyperbus #( .rst_phy_ni ( rst_phy ), .test_mode_i ( test_mode_i ), - .cfg_i ( cfg ), + .cfg_i ( phy_cfg ), .rx_o ( phy_rx ), .rx_valid_o ( phy_rx_valid ), diff --git a/src/hyperbus_axi.sv b/src/hyperbus_axi.sv index 78b3c7d..bac8778 100644 --- a/src/hyperbus_axi.sv +++ b/src/hyperbus_axi.sv @@ -44,11 +44,8 @@ module hyperbus_axi #( output logic trans_valid_o, input logic trans_ready_i, - input rule_t [NumChips-1:0] chip_rules_i, - input logic phys_in_use_i, - input logic which_phy_i, - input logic [4:0] addr_mask_msb_i, - input logic addr_space_i, + input hyperbus_pkg::frontend_cfg_t frontend_cfg_i, + input rule_t [NumChips-1:0] chip_rules_i, output logic trans_active_o ); @@ -163,7 +160,7 @@ module hyperbus_axi #( logic [1:0] phys_in_use; - assign phys_in_use = (NumPhys==2) ? (phys_in_use_i + 1) : 1; + assign phys_in_use = (NumPhys==2) ? (frontend_cfg_i.phys_in_use + 1) : 1; // ============================ // Serialize requests @@ -305,12 +302,12 @@ module hyperbus_axi #( // AX channel: forward, converting unmasked byte to masked word addresses assign trans_o.write = rr_out_req_write; assign trans_o.burst_type = 1'b1; // Wrapping bursts not (yet) supported - assign trans_o.address_space = addr_space_i; + assign trans_o.address_space = frontend_cfg_i.address_space; assign trans_o.address = (NumPhys == 2) ? - (phys_in_use_i ? - ((rr_out_req_ax.addr & ((32'b1 << addr_mask_msb_i) - 1)) >> 2) : - (((rr_out_req_ax.addr & ((32'b1 << addr_mask_msb_i) - 1)) >> 2) << 1)) : - ((rr_out_req_ax.addr & ((32'b1 << addr_mask_msb_i) - 1)) >> 1); + (frontend_cfg_i.phys_in_use ? + ((rr_out_req_ax.addr & ((32'b1 << frontend_cfg_i.address_mask_msb) - 1)) >> 2) : + (((rr_out_req_ax.addr & ((32'b1 << frontend_cfg_i.address_mask_msb) - 1)) >> 2) << 1)) : + ((rr_out_req_ax.addr & ((32'b1 << frontend_cfg_i.address_mask_msb) - 1)) >> 1); // Convert burst length from decremented, unaligned beats to non-decremented, aligned 16-bit words always_comb begin @@ -365,11 +362,11 @@ module hyperbus_axi #( s_rx_error = rx_i.error; s_rx_data_lower_d = s_rx_data_lower_q; merge_r_d = merge_r_q; - if( (NumPhys==2) & (~phys_in_use_i) ) begin + if( (NumPhys==2) & (~frontend_cfg_i.phys_in_use) ) begin if(rx_valid_i & s_rx_ready) begin merge_r_d = merge_r_q + 1; end - if(~which_phy_i) + if(~frontend_cfg_i.which_phy) s_rx_data = { rx_i.data[PhyDataWidth/2-1:0] , s_rx_data_lower_q }; else s_rx_data = { rx_i.data[PhyDataWidth-1:PhyDataWidth/2] , s_rx_data_lower_q }; @@ -378,7 +375,7 @@ module hyperbus_axi #( s_rx_last = rx_i.last & merge_r_q; s_rx_error = rx_i.error; if(~merge_r_q) begin - if(~which_phy_i) + if(~frontend_cfg_i.which_phy) s_rx_data_lower_d = rx_i.data[PhyDataWidth/2-1:0]; else s_rx_data_lower_d = rx_i.data[PhyDataWidth-1:PhyDataWidth/2]; @@ -469,7 +466,7 @@ module hyperbus_axi #( tx_valid_o = s_tx_valid; s_tx_ready = tx_ready_i; split_w_d = split_w_q; - if( (NumPhys==2) & (~phys_in_use_i) ) begin + if( (NumPhys==2) & (~frontend_cfg_i.phys_in_use) ) begin if(s_tx_valid & tx_ready_i) begin split_w_d = split_w_q+1; end diff --git a/src/hyperbus_cfg_regs.sv b/src/hyperbus_cfg_regs.sv index 70ac78e..46310a5 100644 --- a/src/hyperbus_cfg_regs.sv +++ b/src/hyperbus_cfg_regs.sv @@ -7,15 +7,10 @@ module hyperbus_cfg_regs #( parameter int unsigned NumChips = -1, parameter int unsigned NumPhys = -1, - parameter int unsigned RegAddrWidth = -1, parameter int unsigned RegDataWidth = -1, parameter type reg_req_t = logic, parameter type reg_rsp_t = logic, - parameter type rule_t = logic, - parameter logic [RegDataWidth-1:0] RstChipBase = -1, // Base address for all chips - parameter logic [RegDataWidth-1:0] RstChipSpace = -1, // 64 KiB: Current maximum HyperBus device size - parameter int unsigned MinFreqMHz = 100, - parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys,MinFreqMHz) + parameter type rule_t = logic ) ( input logic clk_i, input logic rst_ni, @@ -23,113 +18,168 @@ module hyperbus_cfg_regs #( input reg_req_t reg_req_i, output reg_rsp_t reg_rsp_o, - output hyperbus_pkg::hyper_cfg_t cfg_o, + output hyperbus_pkg::frontend_cfg_t frontend_cfg_o, + output hyperbus_pkg::phy_cfg_t phy_cfg_o, output rule_t [NumChips-1:0] chip_rules_o, input trans_active_i ); `include "common_cells/registers.svh" - // Internal Parameters - localparam int unsigned NumBaseRegs = 12; - localparam int unsigned NumRegs = 2*NumChips + NumBaseRegs; - localparam int unsigned RegsBits = cf_math_pkg::idx_width(NumRegs); - localparam int unsigned RegStrbWidth = RegDataWidth/8; - - // Data and index types - typedef logic [RegsBits-1:0] reg_idx_t; - typedef logic [RegDataWidth-1:0] reg_data_t; - - // Local signals - hyperbus_pkg::hyper_cfg_t cfg_d, cfg_q, cfg_rstval; - reg_data_t [NumChips-1:0][1:0] crange_d, crange_q, crange_rstval; - reg_idx_t sel_reg; - logic sel_reg_mapped; - reg_data_t wmask; - - assign sel_reg = reg_req_i.addr[$clog2(RegStrbWidth) +: RegsBits]; - assign sel_reg_mapped = (sel_reg < NumRegs); - - assign reg_rsp_o.ready = ~trans_active_i; // Config writeable unless currently in transfer - assign reg_rsp_o.error = ~sel_reg_mapped; - - // Read from register - always_comb begin : proc_comb_read - reg_data_t [NumRegs-1:0] rfield; - reg_rsp_o.rdata = '0; - if (sel_reg_mapped) begin - rfield = { - crange_q, - reg_data_t'(cfg_q.csn_to_ck_cycles), - reg_data_t'(cfg_q.t_csh_cycles), - reg_data_t'(cfg_q.which_phy), - reg_data_t'(cfg_q.phys_in_use), - reg_data_t'(cfg_q.address_space), - reg_data_t'(cfg_q.address_mask_msb), - reg_data_t'(cfg_q.t_tx_clk_delay), - reg_data_t'(cfg_q.t_rx_clk_delay), - reg_data_t'(cfg_q.t_read_write_recovery), - reg_data_t'(cfg_q.t_burst_max), - reg_data_t'(cfg_q.en_latency_additional), - reg_data_t'(cfg_q.t_latency_access) - }; - reg_rsp_o.rdata = rfield[sel_reg]; + localparam int unsigned NumChipsMax = 4; + localparam int unsigned NumRegs = 2*NumChipsMax + 12; + localparam int unsigned RegsBits = cf_math_pkg::idx_width(NumRegs); + localparam int unsigned RegStrbWidth = RegDataWidth/8; + + typedef logic [RegsBits-1:0] reg_idx_t; + typedef logic [RegDataWidth-1:0] reg_data_t; + typedef logic [6:0] cfg_addr_t; + typedef logic [31:0] cfg_data_t; + typedef logic [3:0] cfg_strb_t; + + typedef struct packed { + cfg_addr_t addr; + logic write; + cfg_data_t wdata; + cfg_strb_t wstrb; + logic valid; + } cfg_reg_req_t; + + typedef struct packed { + cfg_data_t rdata; + logic error; + logic ready; + } cfg_reg_rsp_t; + + typedef struct packed { + cfg_addr_t paddr; + logic [2:0] pprot; + logic psel; + logic penable; + logic pwrite; + cfg_data_t pwdata; + cfg_strb_t pstrb; + } cfg_apb_req_t; + + typedef struct packed { + logic pready; + cfg_data_t prdata; + logic pslverr; + } cfg_apb_rsp_t; + + hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t cfg_hwif_out; + rule_t [NumChipsMax-1:0] chip_rules_all; + + reg_idx_t sel_reg; + logic sel_reg_mapped; + logic cfg_access_active_d, cfg_access_active_q; + logic cfg_access_open; + + cfg_reg_req_t cfg_reg_req; + cfg_reg_rsp_t cfg_reg_rsp; + cfg_apb_req_t cfg_apb_req; + cfg_apb_rsp_t cfg_apb_rsp; + + assign sel_reg = reg_req_i.addr[$clog2(RegStrbWidth) +: RegsBits]; + assign sel_reg_mapped = (sel_reg < NumRegs); + assign cfg_access_open = ~trans_active_i | cfg_access_active_q; + + assign reg_rsp_o.ready = cfg_access_open & + (~reg_req_i.valid | ~sel_reg_mapped | cfg_reg_rsp.ready); + assign reg_rsp_o.error = ~sel_reg_mapped | cfg_reg_rsp.error; + assign reg_rsp_o.rdata = sel_reg_mapped ? RegDataWidth'(cfg_reg_rsp.rdata) : '0; + + assign cfg_reg_req.valid = reg_req_i.valid & sel_reg_mapped & cfg_access_open; + assign cfg_reg_req.addr = {sel_reg, 2'b00}; + assign cfg_reg_req.write = reg_req_i.write; + assign cfg_reg_req.wdata = 32'(reg_req_i.wdata); + assign cfg_reg_req.wstrb = cfg_strb_t'(reg_req_i.wstrb); + + always_comb begin + cfg_access_active_d = cfg_access_active_q; + if (!cfg_access_active_q && cfg_reg_req.valid) begin + cfg_access_active_d = 1'b1; + end + if (cfg_access_active_q && cfg_reg_rsp.ready) begin + cfg_access_active_d = 1'b0; end end - // Generate write mask - for (genvar i = 0; unsigned'(i) < RegStrbWidth; ++i ) begin : gen_wmask - assign wmask[8*i +: 8] = {8{reg_req_i.wstrb[i]}}; - end + `FFARN(cfg_access_active_q, cfg_access_active_d, 1'b0, clk_i, rst_ni); - // Write to register - always_comb begin : proc_comb_write - logic chip_reg; - logic [$clog2(NumChips)-1:0] sel_chip; - cfg_d = cfg_q; - crange_d = crange_q; - if (reg_req_i.valid & reg_req_i.write & sel_reg_mapped) begin - case (sel_reg) - 'h0: cfg_d.t_latency_access = (~wmask & cfg_q.t_latency_access ) | (wmask & reg_req_i.wdata); - 'h1: cfg_d.en_latency_additional = (~wmask & cfg_q.en_latency_additional ) | (wmask & reg_req_i.wdata); - 'h2: cfg_d.t_burst_max = (~wmask & cfg_q.t_burst_max ) | (wmask & reg_req_i.wdata); - 'h3: cfg_d.t_read_write_recovery = (~wmask & cfg_q.t_read_write_recovery ) | (wmask & reg_req_i.wdata); - 'h4: cfg_d.t_rx_clk_delay = (~wmask & cfg_q.t_rx_clk_delay ) | (wmask & reg_req_i.wdata); - 'h5: cfg_d.t_tx_clk_delay = (~wmask & cfg_q.t_tx_clk_delay ) | (wmask & reg_req_i.wdata); - 'h6: cfg_d.address_mask_msb = (~wmask & cfg_q.address_mask_msb ) | (wmask & reg_req_i.wdata); - 'h7: cfg_d.address_space = (~wmask & cfg_q.address_space ) | (wmask & reg_req_i.wdata); - 'h8: cfg_d.phys_in_use = (NumPhys==1) ? 0 : ( (~wmask & cfg_q.phys_in_use ) | (wmask & reg_req_i.wdata) ); - 'h9: cfg_d.which_phy = (NumPhys==1) ? 0 : ( (~wmask & cfg_q.which_phy ) | (wmask & reg_req_i.wdata) ); - 'ha: cfg_d.t_csh_cycles = (~wmask & cfg_q.t_csh_cycles ) | (wmask & reg_req_i.wdata); - 'hb: cfg_d.csn_to_ck_cycles = (~wmask & cfg_q.csn_to_ck_cycles ) | (wmask & reg_req_i.wdata); - default: begin - {sel_chip, chip_reg} = sel_reg - NumBaseRegs; - crange_d[sel_chip][chip_reg] = (~wmask & crange_q[sel_chip][chip_reg]) | (wmask & reg_req_i.wdata); + always_comb begin + chip_rules_all = '0; + for (int unsigned i = 0; i < NumChipsMax; i++) begin + chip_rules_all[i].idx = unsigned'(i); + unique case (i) + 0: begin + chip_rules_all[i].start_addr = cfg_hwif_out.chip0_base.value.value; + chip_rules_all[i].end_addr = cfg_hwif_out.chip0_bound.value.value; + end + 1: begin + chip_rules_all[i].start_addr = cfg_hwif_out.chip1_base.value.value; + chip_rules_all[i].end_addr = cfg_hwif_out.chip1_bound.value.value; + end + 2: begin + chip_rules_all[i].start_addr = cfg_hwif_out.chip2_base.value.value; + chip_rules_all[i].end_addr = cfg_hwif_out.chip2_bound.value.value; + end + 3: begin + chip_rules_all[i].start_addr = cfg_hwif_out.chip3_base.value.value; + chip_rules_all[i].end_addr = cfg_hwif_out.chip3_bound.value.value; end - endcase // sel_reg + default:; + endcase end end - for (genvar i = 0; unsigned'(i) < NumChips; i++) begin : gen_crange_rstval - assign crange_rstval[i][0] = RstChipBase + (RstChipSpace * i); - assign crange_rstval[i][1] = RstChipBase + (RstChipSpace * (i+1)); // Address decoder: end noninclusive - end - - // Registers - `FFARN(cfg_q, cfg_d, RstCfg, clk_i, rst_ni); - `FFARN(crange_q, crange_d, crange_rstval, clk_i, rst_ni); - - // Outputs - assign cfg_o = cfg_q; - for (genvar i = 0; unsigned'(i) < NumChips; ++i) begin : gen_crange_out - assign chip_rules_o[i].idx = unsigned'(i); // No overlap: keep indices sequential - assign chip_rules_o[i].start_addr = crange_q[i][0]; - assign chip_rules_o[i].end_addr = crange_q[i][1]; + reg_to_apb #( + .reg_req_t ( cfg_reg_req_t ), + .reg_rsp_t ( cfg_reg_rsp_t ), + .apb_req_t ( cfg_apb_req_t ), + .apb_rsp_t ( cfg_apb_rsp_t ) + ) i_reg_to_apb ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .reg_req_i ( cfg_reg_req ), + .reg_rsp_o ( cfg_reg_rsp ), + .apb_req_o ( cfg_apb_req ), + .apb_rsp_i ( cfg_apb_rsp ) + ); + + hyperbus_cfg_regblock i_cfg_regblock ( + .clk ( clk_i ), + .arst_n ( rst_ni ), + .s_apb_psel ( cfg_apb_req.psel ), + .s_apb_penable ( cfg_apb_req.penable ), + .s_apb_pwrite ( cfg_apb_req.pwrite ), + .s_apb_pprot ( cfg_apb_req.pprot ), + .s_apb_paddr ( cfg_apb_req.paddr ), + .s_apb_pwdata ( cfg_apb_req.pwdata ), + .s_apb_pstrb ( cfg_apb_req.pstrb ), + .s_apb_pready ( cfg_apb_rsp.pready ), + .s_apb_prdata ( cfg_apb_rsp.prdata ), + .s_apb_pslverr ( cfg_apb_rsp.pslverr ), + .hwif_out ( cfg_hwif_out ) + ); + + assign frontend_cfg_o = hyperbus_pkg::hwif_to_frontend_cfg(cfg_hwif_out, NumPhys == 1); + assign phy_cfg_o = hyperbus_pkg::hwif_to_phy_cfg(cfg_hwif_out, NumPhys == 1); + + for (genvar i = 0; unsigned'(i) < NumChipsMax; i++) begin : gen_chip_rules + if (i < NumChips) begin : gen_active + assign chip_rules_o[i] = chip_rules_all[i]; + end else begin : gen_inactive + logic unused_chip_rule; + assign unused_chip_rule = ^chip_rules_all[i]; + end end // pragma translate_off `ifndef VERILATOR - initial assert (RegDataWidth >= 16 && $countones(RegDataWidth) == 1) - else $error("RegDataWidth must be a power of two bigger than 16."); + initial assert (RegDataWidth == 32) + else $error("Generated HyperBus config registers require 32-bit RegDataWidth."); + initial assert (NumChips <= NumChipsMax) + else $error("Generated HyperBus config registers support up to four chips."); `endif // pragma translate_on diff --git a/src/hyperbus_phy.sv b/src/hyperbus_phy.sv index 43a5a8d..11c9f84 100644 --- a/src/hyperbus_phy.sv +++ b/src/hyperbus_phy.sv @@ -22,7 +22,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( input logic rst_ni, input logic test_mode_i, // Config registers - input hyper_cfg_t cfg_i, + input phy_cfg_t cfg_i, // PHY control status output logic busy_o, // Transactions @@ -133,7 +133,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( .tx_data_oe_i ( trx_tx_data_oe ), .tx_rwds_i ( trx_tx_rwds ), .tx_rwds_oe_i ( trx_tx_rwds_oe ), - .rx_clk_delay_i ( cfg_i.t_rx_clk_delay ), + .rx_clk_delay_i ( cfg_i.chip.t_rx_clk_delay ), .rx_clk_set_i ( trx_rx_clk_set ), .rx_clk_reset_i ( trx_rx_clk_reset ), .rx_data_o ( trx_rx_data ), @@ -221,8 +221,8 @@ module hyperbus_phy import hyperbus_pkg::*; #( // Auxiliary control signals assign ctl_write_zero_lat = tf_q.address_space & tf_q.write; - // cfg_i.en_latency_additional overwrites the sampled RWDS value. - assign ctl_add_latency = trx_rwds_sample | cfg_i.en_latency_additional; + // cfg_i.chip.en_latency_additional overwrites the sampled RWDS value. + assign ctl_add_latency = trx_rwds_sample | cfg_i.chip.en_latency_additional; assign ctl_tf_burst_last = (tf_q.burst == 1) || (tf_q.burst == phys_in_use); assign ctl_tf_burst_done = (tf_q.burst == 0); @@ -272,11 +272,11 @@ module hyperbus_phy import hyperbus_pkg::*; #( cs_d = trans_cs_i; add_latency_d = 1'b0; - if(cfg_i.csn_to_ck_cycles != 0) begin + if(cfg_i.chip.csn_to_ck_cycles != 0) begin // assert CS but delay hyper_ck to allow more time // for memory to drive RWDS (to satisfy t_DSV) state_d = DelayCK; - timer_d = cfg_i.csn_to_ck_cycles -1; + timer_d = cfg_i.chip.csn_to_ck_cycles -1; end else begin // max throughput when memory RWDS signal arrives early state_d = SendCA; @@ -305,10 +305,10 @@ module hyperbus_phy import hyperbus_pkg::*; #( trx_rwds_sample_ena = ~ctl_write_zero_lat; if (ctl_timer_zero) begin if (ctl_write_zero_lat) begin - timer_d = cfg_i.t_burst_max; + timer_d = cfg_i.chip.t_burst_max; state_d = Write; end else begin - timer_d = TimerWidth'(cfg_i.t_latency_access); + timer_d = TimerWidth'(cfg_i.chip.t_latency_access); add_latency_d = ctl_add_latency; state_d = WaitLatAccess; end @@ -325,7 +325,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( if (~add_latency_q) begin // Substract cycle for last CA and another for state delay if(ctl_timer_two) begin - timer_d = cfg_i.t_burst_max; + timer_d = cfg_i.chip.t_burst_max; // Switch to write or read phase and already start // turnaround of tri-state driver (depending on latency // config and if read or write transaction). @@ -345,7 +345,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( end else if (ctl_timer_one) begin // instead of going to 0, add another latency count state_d = WaitAddLatAccess; - timer_d = TimerWidth'(cfg_i.t_latency_access); + timer_d = TimerWidth'(cfg_i.chip.t_latency_access); add_latency_d = 1'b0; end end @@ -355,7 +355,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( trx_clk_ena = 1'b1; trx_tx_data_oe = 1'b1; if (ctl_timer_two) begin - timer_d = cfg_i.t_burst_max; + timer_d = cfg_i.chip.t_burst_max; if (tf_q.write) begin state_d = Write; trx_tx_data_oe = 1'b1; @@ -376,13 +376,13 @@ module hyperbus_phy import hyperbus_pkg::*; #( tf_d.burst = tf_q.burst - phys_in_use; tf_d.address = tf_q.address + 1; if (ctl_tf_burst_last) begin - timer_d = cfg_i.t_csh_cycles; + timer_d = cfg_i.chip.t_csh_cycles; state_d = WaitXfer; end end // Force-terminate access on burst time limit if (ctl_timer_one) begin - timer_d = cfg_i.t_csh_cycles; + timer_d = cfg_i.chip.t_csh_cycles; state_d = WaitXfer; end end @@ -398,13 +398,13 @@ module hyperbus_phy import hyperbus_pkg::*; #( tf_d.address = tf_q.address + 1; if (ctl_tf_burst_last) begin b_pending_set = 1'b1; - timer_d = cfg_i.t_csh_cycles; + timer_d = cfg_i.chip.t_csh_cycles; state_d = WaitXfer; end end // Force-terminate access on burst time limit if (ctl_timer_one) begin - timer_d = cfg_i.t_csh_cycles; + timer_d = cfg_i.chip.t_csh_cycles; state_d = WaitXfer; end end @@ -412,7 +412,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( // Wait for FFed Clock and output to stop // May have to be prolonged for potential future devices with t_CSH > 0 if (ctl_timer_zero) begin - timer_d = cfg_i.t_read_write_recovery; + timer_d = cfg_i.chip.t_read_write_recovery; state_d = WaitRWR; end end diff --git a/src/hyperbus_phy_if.sv b/src/hyperbus_phy_if.sv index ac165af..baf51f7 100644 --- a/src/hyperbus_phy_if.sv +++ b/src/hyperbus_phy_if.sv @@ -22,7 +22,7 @@ module hyperbus_phy_if import hyperbus_pkg::*; #( input logic rst_phy_ni, input logic test_mode_i, // Config registers - input hyper_cfg_t cfg_i, + input phy_cfg_t cfg_i, // Transactions input logic trans_valid_i, output logic trans_ready_o, diff --git a/src/hyperbus_pkg.sv b/src/hyperbus_pkg.sv index 3f97a7a..664e647 100644 --- a/src/hyperbus_pkg.sv +++ b/src/hyperbus_pkg.sv @@ -15,14 +15,57 @@ package hyperbus_pkg; logic [15:0] t_burst_max; logic [3:0] t_read_write_recovery; logic [7:0] t_rx_clk_delay; - logic [7:0] t_tx_clk_delay; + logic [3:0] t_csh_cycles; // configurable t_CSH for high-frequency operation (200 MHz HyperRAM) + logic [3:0] csn_to_ck_cycles; // delay hyper_ck after CS is asserted (more time for t_DSV) + } chip_phy_cfg_t; + + typedef struct packed { logic [4:0] address_mask_msb; logic address_space; logic phys_in_use; logic which_phy; - logic [3:0] t_csh_cycles; // configurable t_CSH for high-frequency operation (200 MHz HyperRAM) - logic [3:0] csn_to_ck_cycles; // delay hyper_ck after CS is asserted (more time for t_DSV) - } hyper_cfg_t; + } frontend_cfg_t; + + typedef struct packed { + chip_phy_cfg_t chip; + logic [7:0] t_tx_clk_delay; + logic phys_in_use; + logic which_phy; + } phy_cfg_t; + + function automatic frontend_cfg_t hwif_to_frontend_cfg( + input hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t hwif, + input logic num_phys_one + ); + frontend_cfg_t cfg; + + cfg.address_mask_msb = hwif.address_mask_msb.value.value; + cfg.address_space = hwif.address_space.value.value; + cfg.phys_in_use = num_phys_one ? 1'b0 : hwif.phys_in_use.value.value; + cfg.which_phy = num_phys_one ? 1'b0 : hwif.which_phy.value.value; + + return cfg; + endfunction + + function automatic phy_cfg_t hwif_to_phy_cfg( + input hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t hwif, + input logic num_phys_one + ); + phy_cfg_t cfg; + + cfg.chip.t_latency_access = hwif.t_latency_access.value.value; + cfg.chip.en_latency_additional = hwif.en_latency_additional.value.value; + cfg.chip.t_burst_max = hwif.t_burst_max.value.value; + cfg.chip.t_read_write_recovery = hwif.t_read_write_recovery.value.value; + cfg.chip.t_rx_clk_delay = hwif.t_rx_clk_delay.value.value; + cfg.chip.t_csh_cycles = hwif.t_csh_cycles.value.value; + cfg.chip.csn_to_ck_cycles = hwif.csn_to_ck_cycles.value.value; + cfg.t_tx_clk_delay = hwif.t_tx_clk_delay.value.value; + cfg.phys_in_use = num_phys_one ? 1'b0 : hwif.phys_in_use.value.value; + cfg.which_phy = num_phys_one ? 1'b0 : hwif.which_phy.value.value; + + return cfg; + endfunction typedef struct packed { logic write; // transaction is a write @@ -60,28 +103,4 @@ package hyperbus_pkg; logic [2:0] addr_lower; } hyper_phy_ca_t; - - // Register reset values - function automatic hyper_cfg_t gen_RstCfg(input int unsigned NumPhys, input int unsigned MinFreqMhz); - // MinFreqMHz = 100 is spec-compliant and should not be changed. - // It can be lowered if this frequency is not reachable in operation (this may not work with certain HyperBus devices). - // >200 is outside the spec and is unlikely to work with any HyperBus devices - automatic hyper_cfg_t cfg = hyper_cfg_t'{ - t_latency_access: 'h6, - en_latency_additional: 'b0, - t_burst_max: ((MinFreqMhz*35)/10), // t_{csm}: At lowest legal clock (100 MHz) 3.5us (0.5us safety margin) - t_read_write_recovery: 'h6, - t_rx_clk_delay: 'h10, - t_tx_clk_delay: 'h10, - address_mask_msb: 'd25, // 2^(address mask MSB) = single chip size [bytes] - address_space: 'b0, - phys_in_use: NumPhys-1, - which_phy: NumPhys-1, - t_csh_cycles: 'h1, - csn_to_ck_cycles: 4'h0 // additional cycles from CS_N going low to start of hyper_ck - }; - - return cfg; - endfunction - endpackage diff --git a/src/hyperbus_synth_wrap.sv b/src/hyperbus_synth_wrap.sv index 8b1935e..e7fc312 100644 --- a/src/hyperbus_synth_wrap.sv +++ b/src/hyperbus_synth_wrap.sv @@ -145,7 +145,6 @@ module hyperbus_lint_wrap #( .axi_ar_chan_t ( ar_chan_t ), .axi_r_chan_t ( r_chan_t ), .axi_aw_chan_t ( aw_chan_t ), - .RegAddrWidth ( RegAddrWidth ), .RegDataWidth ( RegDataWidth ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), diff --git a/test/dut_if.sv b/test/dut_if.sv index 232b541..e63d9e9 100644 --- a/test/dut_if.sv +++ b/test/dut_if.sv @@ -137,7 +137,6 @@ module dut_if .axi_b_chan_t ( axi_b_chan_t ), .axi_ar_chan_t ( axi_ar_chan_t ), .axi_r_chan_t ( axi_r_chan_t ), - .RegAddrWidth ( RegAw ), .RegDataWidth ( RegDw ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), diff --git a/test/fixture_hyperbus.sv b/test/fixture_hyperbus.sv index 9bd24b5..3967143 100644 --- a/test/fixture_hyperbus.sv +++ b/test/fixture_hyperbus.sv @@ -180,7 +180,6 @@ module fixture_hyperbus #( .axi_b_chan_t ( b_chan_t ), .axi_ar_chan_t ( ar_chan_t ), .axi_r_chan_t ( r_chan_t ), - .RegAddrWidth ( RegAw ), .RegDataWidth ( RegDw ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), From 0629bab3882f9ecc64cc5246039b4b695414abf3 Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Tue, 23 Jun 2026 17:08:23 +0200 Subject: [PATCH 03/11] Use named RDL register declarations --- src/regs/hyperbus_cfg_regs.rdl | 101 ++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 40 deletions(-) diff --git a/src/regs/hyperbus_cfg_regs.rdl b/src/regs/hyperbus_cfg_regs.rdl index 5c27572..5e49b7d 100644 --- a/src/regs/hyperbus_cfg_regs.rdl +++ b/src/regs/hyperbus_cfg_regs.rdl @@ -1,101 +1,122 @@ addrmap hyperbus_cfg_regs { - reg { + reg t_latency_access { desc = "Initial latency cycles before read or write data."; field { sw = rw; hw = r; reset = 4'h6; } value[3:0]; - } t_latency_access @ 0x00; + }; - reg { + reg en_latency_additional { desc = "Enable additional latency cycles when requested by RWDS."; field { sw = rw; hw = r; reset = 1'b0; } value; - } en_latency_additional @ 0x04; + }; - reg { + reg t_burst_max { desc = "Maximum continuous burst length before the PHY restarts a transfer."; field { sw = rw; hw = r; reset = 16'd350; } value[15:0]; - } t_burst_max @ 0x08; + }; - reg { + reg t_read_write_recovery { desc = "Recovery cycles inserted between read and write phases."; field { sw = rw; hw = r; reset = 4'h6; } value[3:0]; - } t_read_write_recovery @ 0x0c; + }; - reg { + reg t_rx_clk_delay { desc = "RX sampling delay-line tap setting."; field { sw = rw; hw = r; reset = 8'h10; } value[7:0]; - } t_rx_clk_delay @ 0x10; + }; - reg { + reg t_tx_clk_delay { desc = "TX clock delay-line tap setting."; field { sw = rw; hw = r; reset = 8'h10; } value[7:0]; - } t_tx_clk_delay @ 0x14; + }; - reg { + reg address_mask_msb { desc = "Most-significant address bit used by frontend address packing."; field { sw = rw; hw = r; reset = 5'd25; } value[4:0]; - } address_mask_msb @ 0x18; + }; - reg { + reg address_space { desc = "Select HyperRAM or HyperFlash address-space interpretation."; field { sw = rw; hw = r; reset = 1'b0; } value; - } address_space @ 0x1c; + }; - reg { + reg phys_in_use { desc = "Select whether one or both PHY lanes are active."; field { sw = rw; hw = r; reset = 1'b1; } value; - } phys_in_use @ 0x20; + }; - reg { + reg which_phy { desc = "Select the PHY lane used when only one PHY is active."; field { sw = rw; hw = r; reset = 1'b1; } value; - } which_phy @ 0x24; + }; - reg { + reg t_csh_cycles { desc = "Chip-select high time between transfers."; field { sw = rw; hw = r; reset = 4'h1; } value[3:0]; - } t_csh_cycles @ 0x28; + }; - reg { + reg csn_to_ck_cycles { desc = "Delay cycles from chip-select assertion to clock start."; field { sw = rw; hw = r; reset = 4'h0; } value[3:0]; - } csn_to_ck_cycles @ 0x2c; + }; - reg { + reg chip0_base { desc = "Inclusive base address for chip-select 0 decoding."; field { sw = rw; hw = r; reset = 32'h0000_0000; } value[31:0]; - } chip0_base @ 0x30; + }; - reg { + reg chip0_bound { desc = "Exclusive bound address for chip-select 0 decoding."; field { sw = rw; hw = r; reset = 32'h0001_0000; } value[31:0]; - } chip0_bound @ 0x34; + }; - reg { + reg chip1_base { desc = "Inclusive base address for chip-select 1 decoding."; field { sw = rw; hw = r; reset = 32'h0001_0000; } value[31:0]; - } chip1_base @ 0x38; + }; - reg { + reg chip1_bound { desc = "Exclusive bound address for chip-select 1 decoding."; field { sw = rw; hw = r; reset = 32'h0002_0000; } value[31:0]; - } chip1_bound @ 0x3c; + }; - reg { + reg chip2_base { desc = "Inclusive base address for chip-select 2 decoding."; field { sw = rw; hw = r; reset = 32'h0002_0000; } value[31:0]; - } chip2_base @ 0x40; + }; - reg { + reg chip2_bound { desc = "Exclusive bound address for chip-select 2 decoding."; field { sw = rw; hw = r; reset = 32'h0003_0000; } value[31:0]; - } chip2_bound @ 0x44; + }; - reg { + reg chip3_base { desc = "Inclusive base address for chip-select 3 decoding."; field { sw = rw; hw = r; reset = 32'h0003_0000; } value[31:0]; - } chip3_base @ 0x48; + }; - reg { + reg chip3_bound { desc = "Exclusive bound address for chip-select 3 decoding."; field { sw = rw; hw = r; reset = 32'h0004_0000; } value[31:0]; - } chip3_bound @ 0x4c; + }; + + t_latency_access t_latency_access @ 0x00; + en_latency_additional en_latency_additional @ 0x04; + t_burst_max t_burst_max @ 0x08; + t_read_write_recovery t_read_write_recovery @ 0x0c; + t_rx_clk_delay t_rx_clk_delay @ 0x10; + t_tx_clk_delay t_tx_clk_delay @ 0x14; + address_mask_msb address_mask_msb @ 0x18; + address_space address_space @ 0x1c; + phys_in_use phys_in_use @ 0x20; + which_phy which_phy @ 0x24; + t_csh_cycles t_csh_cycles @ 0x28; + csn_to_ck_cycles csn_to_ck_cycles @ 0x2c; + chip0_base chip0_base @ 0x30; + chip0_bound chip0_bound @ 0x34; + chip1_base chip1_base @ 0x38; + chip1_bound chip1_bound @ 0x3c; + chip2_base chip2_base @ 0x40; + chip2_bound chip2_bound @ 0x44; + chip3_base chip3_base @ 0x48; + chip3_bound chip3_bound @ 0x4c; }; From a35300e4d98bbb9e8aa81a2767a877409e2880d5 Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Tue, 21 Jul 2026 14:52:51 +0200 Subject: [PATCH 04/11] Extend generated HyperBus configuration --- docs/regs/hyperbus_cfg_regs.md | 198 ++++++++-- include/hyperbus_cfg_regs.h | 145 +++++-- src/hyperbus_cfg_regs.sv | 54 ++- src/hyperbus_pkg.sv | 10 +- src/regs/hyperbus_cfg_regblock.sv | 530 ++++++++++++++++++++++---- src/regs/hyperbus_cfg_regblock_pkg.sv | 112 +++++- src/regs/hyperbus_cfg_regs.rdl | 112 +++++- 7 files changed, 958 insertions(+), 203 deletions(-) diff --git a/docs/regs/hyperbus_cfg_regs.md b/docs/regs/hyperbus_cfg_regs.md index 8e995aa..326f98d 100644 --- a/docs/regs/hyperbus_cfg_regs.md +++ b/docs/regs/hyperbus_cfg_regs.md @@ -9,7 +9,9 @@ Don't override. Generated from: hyperbus_cfg_regs - Absolute Address: 0x0 - Base Offset: 0x0 -- Size: 0x50 +- Size: 0x78 + +

HyperBus controller configuration. Chip address ranges must be 4 MiB aligned, ordered by chip-select index, and non-overlapping.

|Offset| Identifier |Name| |------|---------------------|----| @@ -33,6 +35,14 @@ Don't override. Generated from: hyperbus_cfg_regs | 0x44 | chip2_bound | — | | 0x48 | chip3_base | — | | 0x4C | chip3_bound | — | +| 0x58 | chip4_base | — | +| 0x5C | chip4_bound | — | +| 0x60 | chip5_base | — | +| 0x64 | chip5_bound | — | +| 0x68 | chip6_base | — | +| 0x6C | chip6_bound | — | +| 0x70 | chip7_base | — | +| 0x74 | chip7_bound | — | ### t_latency_access register @@ -88,11 +98,20 @@ Don't override. Generated from: hyperbus_cfg_regs - Base Offset: 0x10 - Size: 0x4 -

RX sampling delay-line tap setting.

+

RX sampling delay with fine taps in bits 4:0 and coarse taps in bits 7:5.

|Bits|Identifier|Access|Reset|Name| |----|----------|------|-----|----| -| 7:0| value | rw | 0x10| — | +| 4:0| fine | rw | 0x10| — | +| 7:5| coarse | rw | 0x0 | — | + +#### fine field + +

Fine delay-line tap setting, nominally about 78 ps per tap.

+ +#### coarse field + +

Coarse delay-line tap setting with an implementation-defined step size.

### t_tx_clk_delay register @@ -100,11 +119,20 @@ Don't override. Generated from: hyperbus_cfg_regs - Base Offset: 0x14 - Size: 0x4 -

TX clock delay-line tap setting.

+

TX clock delay with fine taps in bits 4:0 and coarse taps in bits 7:5.

|Bits|Identifier|Access|Reset|Name| |----|----------|------|-----|----| -| 7:0| value | rw | 0x10| — | +| 4:0| fine | rw | 0x10| — | +| 7:5| coarse | rw | 0x0 | — | + +#### fine field + +

Fine delay-line tap setting, nominally about 78 ps per tap.

+ +#### coarse field + +

Coarse delay-line tap setting with an implementation-defined step size.

### address_mask_msb register @@ -184,11 +212,11 @@ Don't override. Generated from: hyperbus_cfg_regs - Base Offset: 0x30 - Size: 0x4 -

Inclusive base address for chip-select 0 decoding.

+

Inclusive 4 MiB-aligned address for chip-select 0 decoding.

-|Bits|Identifier|Access|Reset|Name| -|----|----------|------|-----|----| -|31:0| value | rw | 0x0 | — | +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x0 | — | ### chip0_bound register @@ -196,11 +224,11 @@ Don't override. Generated from: hyperbus_cfg_regs - Base Offset: 0x34 - Size: 0x4 -

Exclusive bound address for chip-select 0 decoding.

+

Exclusive 4 MiB-aligned address for chip-select 0 decoding.

-|Bits|Identifier|Access| Reset |Name| -|----|----------|------|-------|----| -|31:0| value | rw |0x10000| — | +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x1 | — | ### chip1_base register @@ -208,11 +236,11 @@ Don't override. Generated from: hyperbus_cfg_regs - Base Offset: 0x38 - Size: 0x4 -

Inclusive base address for chip-select 1 decoding.

+

Inclusive 4 MiB-aligned address for chip-select 1 decoding.

-|Bits|Identifier|Access| Reset |Name| -|----|----------|------|-------|----| -|31:0| value | rw |0x10000| — | +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x1 | — | ### chip1_bound register @@ -220,11 +248,11 @@ Don't override. Generated from: hyperbus_cfg_regs - Base Offset: 0x3C - Size: 0x4 -

Exclusive bound address for chip-select 1 decoding.

+

Exclusive 4 MiB-aligned address for chip-select 1 decoding.

-|Bits|Identifier|Access| Reset |Name| -|----|----------|------|-------|----| -|31:0| value | rw |0x20000| — | +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x2 | — | ### chip2_base register @@ -232,11 +260,11 @@ Don't override. Generated from: hyperbus_cfg_regs - Base Offset: 0x40 - Size: 0x4 -

Inclusive base address for chip-select 2 decoding.

+

Inclusive 4 MiB-aligned address for chip-select 2 decoding.

-|Bits|Identifier|Access| Reset |Name| -|----|----------|------|-------|----| -|31:0| value | rw |0x20000| — | +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x2 | — | ### chip2_bound register @@ -244,11 +272,11 @@ Don't override. Generated from: hyperbus_cfg_regs - Base Offset: 0x44 - Size: 0x4 -

Exclusive bound address for chip-select 2 decoding.

+

Exclusive 4 MiB-aligned address for chip-select 2 decoding.

-|Bits|Identifier|Access| Reset |Name| -|----|----------|------|-------|----| -|31:0| value | rw |0x30000| — | +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x3 | — | ### chip3_base register @@ -256,11 +284,11 @@ Don't override. Generated from: hyperbus_cfg_regs - Base Offset: 0x48 - Size: 0x4 -

Inclusive base address for chip-select 3 decoding.

+

Inclusive 4 MiB-aligned address for chip-select 3 decoding.

-|Bits|Identifier|Access| Reset |Name| -|----|----------|------|-------|----| -|31:0| value | rw |0x30000| — | +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x3 | — | ### chip3_bound register @@ -268,8 +296,104 @@ Don't override. Generated from: hyperbus_cfg_regs - Base Offset: 0x4C - Size: 0x4 -

Exclusive bound address for chip-select 3 decoding.

+

Exclusive 4 MiB-aligned address for chip-select 3 decoding.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x4 | — | + +### chip4_base register + +- Absolute Address: 0x58 +- Base Offset: 0x58 +- Size: 0x4 + +

Inclusive 4 MiB-aligned address for chip-select 4 decoding.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x4 | — | + +### chip4_bound register + +- Absolute Address: 0x5C +- Base Offset: 0x5C +- Size: 0x4 + +

Exclusive 4 MiB-aligned address for chip-select 4 decoding.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x5 | — | + +### chip5_base register + +- Absolute Address: 0x60 +- Base Offset: 0x60 +- Size: 0x4 + +

Inclusive 4 MiB-aligned address for chip-select 5 decoding.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x5 | — | + +### chip5_bound register + +- Absolute Address: 0x64 +- Base Offset: 0x64 +- Size: 0x4 + +

Exclusive 4 MiB-aligned address for chip-select 5 decoding.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x6 | — | + +### chip6_base register + +- Absolute Address: 0x68 +- Base Offset: 0x68 +- Size: 0x4 + +

Inclusive 4 MiB-aligned address for chip-select 6 decoding.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x6 | — | + +### chip6_bound register + +- Absolute Address: 0x6C +- Base Offset: 0x6C +- Size: 0x4 + +

Exclusive 4 MiB-aligned address for chip-select 6 decoding.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x7 | — | + +### chip7_base register + +- Absolute Address: 0x70 +- Base Offset: 0x70 +- Size: 0x4 + +

Inclusive 4 MiB-aligned address for chip-select 7 decoding.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x7 | — | + +### chip7_bound register + +- Absolute Address: 0x74 +- Base Offset: 0x74 +- Size: 0x4 + +

Exclusive 4 MiB-aligned address for chip-select 7 decoding.

-|Bits|Identifier|Access| Reset |Name| -|----|----------|------|-------|----| -|31:0| value | rw |0x40000| — | +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x8 | — | diff --git a/include/hyperbus_cfg_regs.h b/include/hyperbus_cfg_regs.h index 795ce1b..a5a09ba 100644 --- a/include/hyperbus_cfg_regs.h +++ b/include/hyperbus_cfg_regs.h @@ -35,16 +35,24 @@ extern "C" { #define HYPERBUS_CFG_REGS__T_READ_WRITE_RECOVERY__VALUE_reset 0x6 // reg - hyperbus_cfg_regs::t_rx_clk_delay -#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__VALUE_bm 0xff -#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__VALUE_bw 8 -#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__VALUE_reset 0x10 +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__FINE_bm 0x1f +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__FINE_bp 0 +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__FINE_bw 5 +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__FINE_reset 0x10 +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__COARSE_bm 0xe0 +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__COARSE_bp 5 +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__COARSE_bw 3 +#define HYPERBUS_CFG_REGS__T_RX_CLK_DELAY__COARSE_reset 0x0 // reg - hyperbus_cfg_regs::t_tx_clk_delay -#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__VALUE_bm 0xff -#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__VALUE_bw 8 -#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__VALUE_reset 0x10 +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__FINE_bm 0x1f +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__FINE_bp 0 +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__FINE_bw 5 +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__FINE_reset 0x10 +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__COARSE_bm 0xe0 +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__COARSE_bp 5 +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__COARSE_bw 3 +#define HYPERBUS_CFG_REGS__T_TX_CLK_DELAY__COARSE_reset 0x0 // reg - hyperbus_cfg_regs::address_mask_msb #define HYPERBUS_CFG_REGS__ADDRESS_MASK_MSB__VALUE_bm 0x1f @@ -83,52 +91,100 @@ extern "C" { #define HYPERBUS_CFG_REGS__CSN_TO_CK_CYCLES__VALUE_reset 0x0 // reg - hyperbus_cfg_regs::chip0_base -#define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_bm 0xffffffff -#define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_bw 32 +#define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_bw 10 #define HYPERBUS_CFG_REGS__CHIP0_BASE__VALUE_reset 0x0 // reg - hyperbus_cfg_regs::chip0_bound -#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_bm 0xffffffff -#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_bw 32 -#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_reset 0x10000 +#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP0_BOUND__VALUE_reset 0x1 // reg - hyperbus_cfg_regs::chip1_base -#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_bm 0xffffffff -#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_bw 32 -#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_reset 0x10000 +#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP1_BASE__VALUE_reset 0x1 // reg - hyperbus_cfg_regs::chip1_bound -#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_bm 0xffffffff -#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_bw 32 -#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_reset 0x20000 +#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP1_BOUND__VALUE_reset 0x2 // reg - hyperbus_cfg_regs::chip2_base -#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_bm 0xffffffff -#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_bw 32 -#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_reset 0x20000 +#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP2_BASE__VALUE_reset 0x2 // reg - hyperbus_cfg_regs::chip2_bound -#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_bm 0xffffffff -#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_bw 32 -#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_reset 0x30000 +#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP2_BOUND__VALUE_reset 0x3 // reg - hyperbus_cfg_regs::chip3_base -#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_bm 0xffffffff -#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_bw 32 -#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_reset 0x30000 +#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP3_BASE__VALUE_reset 0x3 // reg - hyperbus_cfg_regs::chip3_bound -#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_bm 0xffffffff -#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_bw 32 -#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_reset 0x40000 +#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_reset 0x4 + +// reg - hyperbus_cfg_regs::chip4_base +#define HYPERBUS_CFG_REGS__CHIP4_BASE__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP4_BASE__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP4_BASE__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP4_BASE__VALUE_reset 0x4 + +// reg - hyperbus_cfg_regs::chip4_bound +#define HYPERBUS_CFG_REGS__CHIP4_BOUND__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP4_BOUND__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP4_BOUND__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP4_BOUND__VALUE_reset 0x5 + +// reg - hyperbus_cfg_regs::chip5_base +#define HYPERBUS_CFG_REGS__CHIP5_BASE__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP5_BASE__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP5_BASE__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP5_BASE__VALUE_reset 0x5 + +// reg - hyperbus_cfg_regs::chip5_bound +#define HYPERBUS_CFG_REGS__CHIP5_BOUND__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP5_BOUND__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP5_BOUND__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP5_BOUND__VALUE_reset 0x6 + +// reg - hyperbus_cfg_regs::chip6_base +#define HYPERBUS_CFG_REGS__CHIP6_BASE__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP6_BASE__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP6_BASE__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP6_BASE__VALUE_reset 0x6 + +// reg - hyperbus_cfg_regs::chip6_bound +#define HYPERBUS_CFG_REGS__CHIP6_BOUND__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP6_BOUND__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP6_BOUND__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP6_BOUND__VALUE_reset 0x7 + +// reg - hyperbus_cfg_regs::chip7_base +#define HYPERBUS_CFG_REGS__CHIP7_BASE__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP7_BASE__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP7_BASE__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP7_BASE__VALUE_reset 0x7 + +// reg - hyperbus_cfg_regs::chip7_bound +#define HYPERBUS_CFG_REGS__CHIP7_BOUND__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__CHIP7_BOUND__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__CHIP7_BOUND__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__CHIP7_BOUND__VALUE_reset 0x8 // addrmap - hyperbus_cfg_regs typedef struct __attribute__ ((__packed__)) { @@ -152,10 +208,19 @@ typedef struct __attribute__ ((__packed__)) { uint32_t chip2_bound; uint32_t chip3_base; uint32_t chip3_bound; + uint8_t RESERVED_50_57[0x8]; + uint32_t chip4_base; + uint32_t chip4_bound; + uint32_t chip5_base; + uint32_t chip5_bound; + uint32_t chip6_base; + uint32_t chip6_bound; + uint32_t chip7_base; + uint32_t chip7_bound; } hyperbus_cfg_regs_t; -static_assert(sizeof(hyperbus_cfg_regs_t) == 0x50, "Packing error"); +static_assert(sizeof(hyperbus_cfg_regs_t) == 0x78, "Packing error"); #ifdef __cplusplus } diff --git a/src/hyperbus_cfg_regs.sv b/src/hyperbus_cfg_regs.sv index 46310a5..84e792f 100644 --- a/src/hyperbus_cfg_regs.sv +++ b/src/hyperbus_cfg_regs.sv @@ -25,8 +25,8 @@ module hyperbus_cfg_regs #( ); `include "common_cells/registers.svh" - localparam int unsigned NumChipsMax = 4; - localparam int unsigned NumRegs = 2*NumChipsMax + 12; + localparam int unsigned NumChipsMax = 8; + localparam int unsigned NumRegs = 30; localparam int unsigned RegsBits = cf_math_pkg::idx_width(NumRegs); localparam int unsigned RegStrbWidth = RegDataWidth/8; @@ -112,20 +112,52 @@ module hyperbus_cfg_regs #( chip_rules_all[i].idx = unsigned'(i); unique case (i) 0: begin - chip_rules_all[i].start_addr = cfg_hwif_out.chip0_base.value.value; - chip_rules_all[i].end_addr = cfg_hwif_out.chip0_bound.value.value; + chip_rules_all[i].start_addr = { + cfg_hwif_out.chip0_base.value.value, 22'b0}; + chip_rules_all[i].end_addr = { + cfg_hwif_out.chip0_bound.value.value, 22'b0}; end 1: begin - chip_rules_all[i].start_addr = cfg_hwif_out.chip1_base.value.value; - chip_rules_all[i].end_addr = cfg_hwif_out.chip1_bound.value.value; + chip_rules_all[i].start_addr = { + cfg_hwif_out.chip1_base.value.value, 22'b0}; + chip_rules_all[i].end_addr = { + cfg_hwif_out.chip1_bound.value.value, 22'b0}; end 2: begin - chip_rules_all[i].start_addr = cfg_hwif_out.chip2_base.value.value; - chip_rules_all[i].end_addr = cfg_hwif_out.chip2_bound.value.value; + chip_rules_all[i].start_addr = { + cfg_hwif_out.chip2_base.value.value, 22'b0}; + chip_rules_all[i].end_addr = { + cfg_hwif_out.chip2_bound.value.value, 22'b0}; end 3: begin - chip_rules_all[i].start_addr = cfg_hwif_out.chip3_base.value.value; - chip_rules_all[i].end_addr = cfg_hwif_out.chip3_bound.value.value; + chip_rules_all[i].start_addr = { + cfg_hwif_out.chip3_base.value.value, 22'b0}; + chip_rules_all[i].end_addr = { + cfg_hwif_out.chip3_bound.value.value, 22'b0}; + end + 4: begin + chip_rules_all[i].start_addr = { + cfg_hwif_out.chip4_base.value.value, 22'b0}; + chip_rules_all[i].end_addr = { + cfg_hwif_out.chip4_bound.value.value, 22'b0}; + end + 5: begin + chip_rules_all[i].start_addr = { + cfg_hwif_out.chip5_base.value.value, 22'b0}; + chip_rules_all[i].end_addr = { + cfg_hwif_out.chip5_bound.value.value, 22'b0}; + end + 6: begin + chip_rules_all[i].start_addr = { + cfg_hwif_out.chip6_base.value.value, 22'b0}; + chip_rules_all[i].end_addr = { + cfg_hwif_out.chip6_bound.value.value, 22'b0}; + end + 7: begin + chip_rules_all[i].start_addr = { + cfg_hwif_out.chip7_base.value.value, 22'b0}; + chip_rules_all[i].end_addr = { + cfg_hwif_out.chip7_bound.value.value, 22'b0}; end default:; endcase @@ -179,7 +211,7 @@ module hyperbus_cfg_regs #( initial assert (RegDataWidth == 32) else $error("Generated HyperBus config registers require 32-bit RegDataWidth."); initial assert (NumChips <= NumChipsMax) - else $error("Generated HyperBus config registers support up to four chips."); + else $error("Generated HyperBus config registers support up to eight chips."); `endif // pragma translate_on diff --git a/src/hyperbus_pkg.sv b/src/hyperbus_pkg.sv index 664e647..1fa6219 100644 --- a/src/hyperbus_pkg.sv +++ b/src/hyperbus_pkg.sv @@ -57,10 +57,16 @@ package hyperbus_pkg; cfg.chip.en_latency_additional = hwif.en_latency_additional.value.value; cfg.chip.t_burst_max = hwif.t_burst_max.value.value; cfg.chip.t_read_write_recovery = hwif.t_read_write_recovery.value.value; - cfg.chip.t_rx_clk_delay = hwif.t_rx_clk_delay.value.value; + cfg.chip.t_rx_clk_delay = { + hwif.t_rx_clk_delay.coarse.value, + hwif.t_rx_clk_delay.fine.value + }; cfg.chip.t_csh_cycles = hwif.t_csh_cycles.value.value; cfg.chip.csn_to_ck_cycles = hwif.csn_to_ck_cycles.value.value; - cfg.t_tx_clk_delay = hwif.t_tx_clk_delay.value.value; + cfg.t_tx_clk_delay = { + hwif.t_tx_clk_delay.coarse.value, + hwif.t_tx_clk_delay.fine.value + }; cfg.phys_in_use = num_phys_one ? 1'b0 : hwif.phys_in_use.value.value; cfg.which_phy = num_phys_one ? 1'b0 : hwif.which_phy.value.value; diff --git a/src/regs/hyperbus_cfg_regblock.sv b/src/regs/hyperbus_cfg_regblock.sv index 5cbcc73..75b0286 100644 --- a/src/regs/hyperbus_cfg_regblock.sv +++ b/src/regs/hyperbus_cfg_regblock.sv @@ -106,6 +106,14 @@ module hyperbus_cfg_regblock ( logic chip2_bound; logic chip3_base; logic chip3_bound; + logic chip4_base; + logic chip4_bound; + logic chip5_base; + logic chip5_bound; + logic chip6_base; + logic chip6_bound; + logic chip7_base; + logic chip7_bound; } decoded_reg_strb_t; decoded_reg_strb_t decoded_reg_strb; logic decoded_err; @@ -140,6 +148,14 @@ module hyperbus_cfg_regblock ( decoded_reg_strb.chip2_bound = cpuif_req_masked & (cpuif_addr == 7'h44); decoded_reg_strb.chip3_base = cpuif_req_masked & (cpuif_addr == 7'h48); decoded_reg_strb.chip3_bound = cpuif_req_masked & (cpuif_addr == 7'h4c); + decoded_reg_strb.chip4_base = cpuif_req_masked & (cpuif_addr == 7'h58); + decoded_reg_strb.chip4_bound = cpuif_req_masked & (cpuif_addr == 7'h5c); + decoded_reg_strb.chip5_base = cpuif_req_masked & (cpuif_addr == 7'h60); + decoded_reg_strb.chip5_bound = cpuif_req_masked & (cpuif_addr == 7'h64); + decoded_reg_strb.chip6_base = cpuif_req_masked & (cpuif_addr == 7'h68); + decoded_reg_strb.chip6_bound = cpuif_req_masked & (cpuif_addr == 7'h6c); + decoded_reg_strb.chip7_base = cpuif_req_masked & (cpuif_addr == 7'h70); + decoded_reg_strb.chip7_bound = cpuif_req_masked & (cpuif_addr == 7'h74); decoded_err = '0; end @@ -180,15 +196,23 @@ module hyperbus_cfg_regblock ( } t_read_write_recovery; struct { struct { - logic [7:0] next; + logic [4:0] next; logic load_next; - } value; + } fine; + struct { + logic [2:0] next; + logic load_next; + } coarse; } t_rx_clk_delay; struct { struct { - logic [7:0] next; + logic [4:0] next; logic load_next; - } value; + } fine; + struct { + logic [2:0] next; + logic load_next; + } coarse; } t_tx_clk_delay; struct { struct { @@ -228,52 +252,100 @@ module hyperbus_cfg_regblock ( } csn_to_ck_cycles; struct { struct { - logic [31:0] next; + logic [9:0] next; logic load_next; } value; } chip0_base; struct { struct { - logic [31:0] next; + logic [9:0] next; logic load_next; } value; } chip0_bound; struct { struct { - logic [31:0] next; + logic [9:0] next; logic load_next; } value; } chip1_base; struct { struct { - logic [31:0] next; + logic [9:0] next; logic load_next; } value; } chip1_bound; struct { struct { - logic [31:0] next; + logic [9:0] next; logic load_next; } value; } chip2_base; struct { struct { - logic [31:0] next; + logic [9:0] next; logic load_next; } value; } chip2_bound; struct { struct { - logic [31:0] next; + logic [9:0] next; logic load_next; } value; } chip3_base; struct { struct { - logic [31:0] next; + logic [9:0] next; logic load_next; } value; } chip3_bound; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } chip4_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } chip4_bound; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } chip5_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } chip5_bound; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } chip6_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } chip6_bound; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } chip7_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } chip7_bound; } field_combo_t; field_combo_t field_combo; @@ -300,13 +372,19 @@ module hyperbus_cfg_regblock ( } t_read_write_recovery; struct { struct { - logic [7:0] value; - } value; + logic [4:0] value; + } fine; + struct { + logic [2:0] value; + } coarse; } t_rx_clk_delay; struct { struct { - logic [7:0] value; - } value; + logic [4:0] value; + } fine; + struct { + logic [2:0] value; + } coarse; } t_tx_clk_delay; struct { struct { @@ -340,44 +418,84 @@ module hyperbus_cfg_regblock ( } csn_to_ck_cycles; struct { struct { - logic [31:0] value; + logic [9:0] value; } value; } chip0_base; struct { struct { - logic [31:0] value; + logic [9:0] value; } value; } chip0_bound; struct { struct { - logic [31:0] value; + logic [9:0] value; } value; } chip1_base; struct { struct { - logic [31:0] value; + logic [9:0] value; } value; } chip1_bound; struct { struct { - logic [31:0] value; + logic [9:0] value; } value; } chip2_base; struct { struct { - logic [31:0] value; + logic [9:0] value; } value; } chip2_bound; struct { struct { - logic [31:0] value; + logic [9:0] value; } value; } chip3_base; struct { struct { - logic [31:0] value; + logic [9:0] value; } value; } chip3_bound; + struct { + struct { + logic [9:0] value; + } value; + } chip4_base; + struct { + struct { + logic [9:0] value; + } value; + } chip4_bound; + struct { + struct { + logic [9:0] value; + } value; + } chip5_base; + struct { + struct { + logic [9:0] value; + } value; + } chip5_bound; + struct { + struct { + logic [9:0] value; + } value; + } chip6_base; + struct { + struct { + logic [9:0] value; + } value; + } chip6_bound; + struct { + struct { + logic [9:0] value; + } value; + } chip7_base; + struct { + struct { + logic [9:0] value; + } value; + } chip7_bound; } field_storage_t; field_storage_t field_storage; @@ -473,52 +591,98 @@ module hyperbus_cfg_regblock ( end end assign hwif_out.t_read_write_recovery.value.value = field_storage.t_read_write_recovery.value.value; - // Field: hyperbus_cfg_regs.t_rx_clk_delay.value + // Field: hyperbus_cfg_regs.t_rx_clk_delay.fine + always_comb begin + automatic logic [4:0] next_c; + automatic logic load_next_c; + next_c = field_storage.t_rx_clk_delay.fine.value; + load_next_c = '0; + if(decoded_reg_strb.t_rx_clk_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.t_rx_clk_delay.fine.value & ~decoded_wr_biten[4:0]) | (decoded_wr_data[4:0] & decoded_wr_biten[4:0]); + load_next_c = '1; + end + field_combo.t_rx_clk_delay.fine.next = next_c; + field_combo.t_rx_clk_delay.fine.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.t_rx_clk_delay.fine.value <= 5'h10; + end else begin + if(field_combo.t_rx_clk_delay.fine.load_next) begin + field_storage.t_rx_clk_delay.fine.value <= field_combo.t_rx_clk_delay.fine.next; + end + end + end + assign hwif_out.t_rx_clk_delay.fine.value = field_storage.t_rx_clk_delay.fine.value; + // Field: hyperbus_cfg_regs.t_rx_clk_delay.coarse always_comb begin - automatic logic [7:0] next_c; + automatic logic [2:0] next_c; automatic logic load_next_c; - next_c = field_storage.t_rx_clk_delay.value.value; + next_c = field_storage.t_rx_clk_delay.coarse.value; load_next_c = '0; if(decoded_reg_strb.t_rx_clk_delay && decoded_req_is_wr) begin // SW write - next_c = (field_storage.t_rx_clk_delay.value.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + next_c = (field_storage.t_rx_clk_delay.coarse.value & ~decoded_wr_biten[7:5]) | (decoded_wr_data[7:5] & decoded_wr_biten[7:5]); load_next_c = '1; end - field_combo.t_rx_clk_delay.value.next = next_c; - field_combo.t_rx_clk_delay.value.load_next = load_next_c; + field_combo.t_rx_clk_delay.coarse.next = next_c; + field_combo.t_rx_clk_delay.coarse.load_next = load_next_c; end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.t_rx_clk_delay.value.value <= 8'h10; + field_storage.t_rx_clk_delay.coarse.value <= 3'h0; end else begin - if(field_combo.t_rx_clk_delay.value.load_next) begin - field_storage.t_rx_clk_delay.value.value <= field_combo.t_rx_clk_delay.value.next; + if(field_combo.t_rx_clk_delay.coarse.load_next) begin + field_storage.t_rx_clk_delay.coarse.value <= field_combo.t_rx_clk_delay.coarse.next; end end end - assign hwif_out.t_rx_clk_delay.value.value = field_storage.t_rx_clk_delay.value.value; - // Field: hyperbus_cfg_regs.t_tx_clk_delay.value + assign hwif_out.t_rx_clk_delay.coarse.value = field_storage.t_rx_clk_delay.coarse.value; + // Field: hyperbus_cfg_regs.t_tx_clk_delay.fine always_comb begin - automatic logic [7:0] next_c; + automatic logic [4:0] next_c; automatic logic load_next_c; - next_c = field_storage.t_tx_clk_delay.value.value; + next_c = field_storage.t_tx_clk_delay.fine.value; load_next_c = '0; if(decoded_reg_strb.t_tx_clk_delay && decoded_req_is_wr) begin // SW write - next_c = (field_storage.t_tx_clk_delay.value.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + next_c = (field_storage.t_tx_clk_delay.fine.value & ~decoded_wr_biten[4:0]) | (decoded_wr_data[4:0] & decoded_wr_biten[4:0]); load_next_c = '1; end - field_combo.t_tx_clk_delay.value.next = next_c; - field_combo.t_tx_clk_delay.value.load_next = load_next_c; + field_combo.t_tx_clk_delay.fine.next = next_c; + field_combo.t_tx_clk_delay.fine.load_next = load_next_c; end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.t_tx_clk_delay.value.value <= 8'h10; + field_storage.t_tx_clk_delay.fine.value <= 5'h10; end else begin - if(field_combo.t_tx_clk_delay.value.load_next) begin - field_storage.t_tx_clk_delay.value.value <= field_combo.t_tx_clk_delay.value.next; + if(field_combo.t_tx_clk_delay.fine.load_next) begin + field_storage.t_tx_clk_delay.fine.value <= field_combo.t_tx_clk_delay.fine.next; end end end - assign hwif_out.t_tx_clk_delay.value.value = field_storage.t_tx_clk_delay.value.value; + assign hwif_out.t_tx_clk_delay.fine.value = field_storage.t_tx_clk_delay.fine.value; + // Field: hyperbus_cfg_regs.t_tx_clk_delay.coarse + always_comb begin + automatic logic [2:0] next_c; + automatic logic load_next_c; + next_c = field_storage.t_tx_clk_delay.coarse.value; + load_next_c = '0; + if(decoded_reg_strb.t_tx_clk_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.t_tx_clk_delay.coarse.value & ~decoded_wr_biten[7:5]) | (decoded_wr_data[7:5] & decoded_wr_biten[7:5]); + load_next_c = '1; + end + field_combo.t_tx_clk_delay.coarse.next = next_c; + field_combo.t_tx_clk_delay.coarse.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.t_tx_clk_delay.coarse.value <= 3'h0; + end else begin + if(field_combo.t_tx_clk_delay.coarse.load_next) begin + field_storage.t_tx_clk_delay.coarse.value <= field_combo.t_tx_clk_delay.coarse.next; + end + end + end + assign hwif_out.t_tx_clk_delay.coarse.value = field_storage.t_tx_clk_delay.coarse.value; // Field: hyperbus_cfg_regs.address_mask_msb.value always_comb begin automatic logic [4:0] next_c; @@ -659,12 +823,12 @@ module hyperbus_cfg_regblock ( assign hwif_out.csn_to_ck_cycles.value.value = field_storage.csn_to_ck_cycles.value.value; // Field: hyperbus_cfg_regs.chip0_base.value always_comb begin - automatic logic [31:0] next_c; + automatic logic [9:0] next_c; automatic logic load_next_c; next_c = field_storage.chip0_base.value.value; load_next_c = '0; if(decoded_reg_strb.chip0_base && decoded_req_is_wr) begin // SW write - next_c = (field_storage.chip0_base.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + next_c = (field_storage.chip0_base.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); load_next_c = '1; end field_combo.chip0_base.value.next = next_c; @@ -672,7 +836,7 @@ module hyperbus_cfg_regblock ( end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.chip0_base.value.value <= 32'h0; + field_storage.chip0_base.value.value <= 10'h0; end else begin if(field_combo.chip0_base.value.load_next) begin field_storage.chip0_base.value.value <= field_combo.chip0_base.value.next; @@ -682,12 +846,12 @@ module hyperbus_cfg_regblock ( assign hwif_out.chip0_base.value.value = field_storage.chip0_base.value.value; // Field: hyperbus_cfg_regs.chip0_bound.value always_comb begin - automatic logic [31:0] next_c; + automatic logic [9:0] next_c; automatic logic load_next_c; next_c = field_storage.chip0_bound.value.value; load_next_c = '0; if(decoded_reg_strb.chip0_bound && decoded_req_is_wr) begin // SW write - next_c = (field_storage.chip0_bound.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + next_c = (field_storage.chip0_bound.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); load_next_c = '1; end field_combo.chip0_bound.value.next = next_c; @@ -695,7 +859,7 @@ module hyperbus_cfg_regblock ( end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.chip0_bound.value.value <= 32'h10000; + field_storage.chip0_bound.value.value <= 10'h1; end else begin if(field_combo.chip0_bound.value.load_next) begin field_storage.chip0_bound.value.value <= field_combo.chip0_bound.value.next; @@ -705,12 +869,12 @@ module hyperbus_cfg_regblock ( assign hwif_out.chip0_bound.value.value = field_storage.chip0_bound.value.value; // Field: hyperbus_cfg_regs.chip1_base.value always_comb begin - automatic logic [31:0] next_c; + automatic logic [9:0] next_c; automatic logic load_next_c; next_c = field_storage.chip1_base.value.value; load_next_c = '0; if(decoded_reg_strb.chip1_base && decoded_req_is_wr) begin // SW write - next_c = (field_storage.chip1_base.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + next_c = (field_storage.chip1_base.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); load_next_c = '1; end field_combo.chip1_base.value.next = next_c; @@ -718,7 +882,7 @@ module hyperbus_cfg_regblock ( end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.chip1_base.value.value <= 32'h10000; + field_storage.chip1_base.value.value <= 10'h1; end else begin if(field_combo.chip1_base.value.load_next) begin field_storage.chip1_base.value.value <= field_combo.chip1_base.value.next; @@ -728,12 +892,12 @@ module hyperbus_cfg_regblock ( assign hwif_out.chip1_base.value.value = field_storage.chip1_base.value.value; // Field: hyperbus_cfg_regs.chip1_bound.value always_comb begin - automatic logic [31:0] next_c; + automatic logic [9:0] next_c; automatic logic load_next_c; next_c = field_storage.chip1_bound.value.value; load_next_c = '0; if(decoded_reg_strb.chip1_bound && decoded_req_is_wr) begin // SW write - next_c = (field_storage.chip1_bound.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + next_c = (field_storage.chip1_bound.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); load_next_c = '1; end field_combo.chip1_bound.value.next = next_c; @@ -741,7 +905,7 @@ module hyperbus_cfg_regblock ( end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.chip1_bound.value.value <= 32'h20000; + field_storage.chip1_bound.value.value <= 10'h2; end else begin if(field_combo.chip1_bound.value.load_next) begin field_storage.chip1_bound.value.value <= field_combo.chip1_bound.value.next; @@ -751,12 +915,12 @@ module hyperbus_cfg_regblock ( assign hwif_out.chip1_bound.value.value = field_storage.chip1_bound.value.value; // Field: hyperbus_cfg_regs.chip2_base.value always_comb begin - automatic logic [31:0] next_c; + automatic logic [9:0] next_c; automatic logic load_next_c; next_c = field_storage.chip2_base.value.value; load_next_c = '0; if(decoded_reg_strb.chip2_base && decoded_req_is_wr) begin // SW write - next_c = (field_storage.chip2_base.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + next_c = (field_storage.chip2_base.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); load_next_c = '1; end field_combo.chip2_base.value.next = next_c; @@ -764,7 +928,7 @@ module hyperbus_cfg_regblock ( end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.chip2_base.value.value <= 32'h20000; + field_storage.chip2_base.value.value <= 10'h2; end else begin if(field_combo.chip2_base.value.load_next) begin field_storage.chip2_base.value.value <= field_combo.chip2_base.value.next; @@ -774,12 +938,12 @@ module hyperbus_cfg_regblock ( assign hwif_out.chip2_base.value.value = field_storage.chip2_base.value.value; // Field: hyperbus_cfg_regs.chip2_bound.value always_comb begin - automatic logic [31:0] next_c; + automatic logic [9:0] next_c; automatic logic load_next_c; next_c = field_storage.chip2_bound.value.value; load_next_c = '0; if(decoded_reg_strb.chip2_bound && decoded_req_is_wr) begin // SW write - next_c = (field_storage.chip2_bound.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + next_c = (field_storage.chip2_bound.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); load_next_c = '1; end field_combo.chip2_bound.value.next = next_c; @@ -787,7 +951,7 @@ module hyperbus_cfg_regblock ( end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.chip2_bound.value.value <= 32'h30000; + field_storage.chip2_bound.value.value <= 10'h3; end else begin if(field_combo.chip2_bound.value.load_next) begin field_storage.chip2_bound.value.value <= field_combo.chip2_bound.value.next; @@ -797,12 +961,12 @@ module hyperbus_cfg_regblock ( assign hwif_out.chip2_bound.value.value = field_storage.chip2_bound.value.value; // Field: hyperbus_cfg_regs.chip3_base.value always_comb begin - automatic logic [31:0] next_c; + automatic logic [9:0] next_c; automatic logic load_next_c; next_c = field_storage.chip3_base.value.value; load_next_c = '0; if(decoded_reg_strb.chip3_base && decoded_req_is_wr) begin // SW write - next_c = (field_storage.chip3_base.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + next_c = (field_storage.chip3_base.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); load_next_c = '1; end field_combo.chip3_base.value.next = next_c; @@ -810,7 +974,7 @@ module hyperbus_cfg_regblock ( end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.chip3_base.value.value <= 32'h30000; + field_storage.chip3_base.value.value <= 10'h3; end else begin if(field_combo.chip3_base.value.load_next) begin field_storage.chip3_base.value.value <= field_combo.chip3_base.value.next; @@ -820,12 +984,12 @@ module hyperbus_cfg_regblock ( assign hwif_out.chip3_base.value.value = field_storage.chip3_base.value.value; // Field: hyperbus_cfg_regs.chip3_bound.value always_comb begin - automatic logic [31:0] next_c; + automatic logic [9:0] next_c; automatic logic load_next_c; next_c = field_storage.chip3_bound.value.value; load_next_c = '0; if(decoded_reg_strb.chip3_bound && decoded_req_is_wr) begin // SW write - next_c = (field_storage.chip3_bound.value.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + next_c = (field_storage.chip3_bound.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); load_next_c = '1; end field_combo.chip3_bound.value.next = next_c; @@ -833,7 +997,7 @@ module hyperbus_cfg_regblock ( end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.chip3_bound.value.value <= 32'h40000; + field_storage.chip3_bound.value.value <= 10'h4; end else begin if(field_combo.chip3_bound.value.load_next) begin field_storage.chip3_bound.value.value <= field_combo.chip3_bound.value.next; @@ -841,6 +1005,190 @@ module hyperbus_cfg_regblock ( end end assign hwif_out.chip3_bound.value.value = field_storage.chip3_bound.value.value; + // Field: hyperbus_cfg_regs.chip4_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip4_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip4_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip4_base.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); + load_next_c = '1; + end + field_combo.chip4_base.value.next = next_c; + field_combo.chip4_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip4_base.value.value <= 10'h4; + end else begin + if(field_combo.chip4_base.value.load_next) begin + field_storage.chip4_base.value.value <= field_combo.chip4_base.value.next; + end + end + end + assign hwif_out.chip4_base.value.value = field_storage.chip4_base.value.value; + // Field: hyperbus_cfg_regs.chip4_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip4_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip4_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip4_bound.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); + load_next_c = '1; + end + field_combo.chip4_bound.value.next = next_c; + field_combo.chip4_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip4_bound.value.value <= 10'h5; + end else begin + if(field_combo.chip4_bound.value.load_next) begin + field_storage.chip4_bound.value.value <= field_combo.chip4_bound.value.next; + end + end + end + assign hwif_out.chip4_bound.value.value = field_storage.chip4_bound.value.value; + // Field: hyperbus_cfg_regs.chip5_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip5_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip5_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip5_base.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); + load_next_c = '1; + end + field_combo.chip5_base.value.next = next_c; + field_combo.chip5_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip5_base.value.value <= 10'h5; + end else begin + if(field_combo.chip5_base.value.load_next) begin + field_storage.chip5_base.value.value <= field_combo.chip5_base.value.next; + end + end + end + assign hwif_out.chip5_base.value.value = field_storage.chip5_base.value.value; + // Field: hyperbus_cfg_regs.chip5_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip5_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip5_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip5_bound.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); + load_next_c = '1; + end + field_combo.chip5_bound.value.next = next_c; + field_combo.chip5_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip5_bound.value.value <= 10'h6; + end else begin + if(field_combo.chip5_bound.value.load_next) begin + field_storage.chip5_bound.value.value <= field_combo.chip5_bound.value.next; + end + end + end + assign hwif_out.chip5_bound.value.value = field_storage.chip5_bound.value.value; + // Field: hyperbus_cfg_regs.chip6_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip6_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip6_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip6_base.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); + load_next_c = '1; + end + field_combo.chip6_base.value.next = next_c; + field_combo.chip6_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip6_base.value.value <= 10'h6; + end else begin + if(field_combo.chip6_base.value.load_next) begin + field_storage.chip6_base.value.value <= field_combo.chip6_base.value.next; + end + end + end + assign hwif_out.chip6_base.value.value = field_storage.chip6_base.value.value; + // Field: hyperbus_cfg_regs.chip6_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip6_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip6_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip6_bound.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); + load_next_c = '1; + end + field_combo.chip6_bound.value.next = next_c; + field_combo.chip6_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip6_bound.value.value <= 10'h7; + end else begin + if(field_combo.chip6_bound.value.load_next) begin + field_storage.chip6_bound.value.value <= field_combo.chip6_bound.value.next; + end + end + end + assign hwif_out.chip6_bound.value.value = field_storage.chip6_bound.value.value; + // Field: hyperbus_cfg_regs.chip7_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip7_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip7_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip7_base.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); + load_next_c = '1; + end + field_combo.chip7_base.value.next = next_c; + field_combo.chip7_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip7_base.value.value <= 10'h7; + end else begin + if(field_combo.chip7_base.value.load_next) begin + field_storage.chip7_base.value.value <= field_combo.chip7_base.value.next; + end + end + end + assign hwif_out.chip7_base.value.value = field_storage.chip7_base.value.value; + // Field: hyperbus_cfg_regs.chip7_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip7_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip7_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip7_bound.value.value & ~decoded_wr_biten[31:22]) | (decoded_wr_data[31:22] & decoded_wr_biten[31:22]); + load_next_c = '1; + end + field_combo.chip7_bound.value.next = next_c; + field_combo.chip7_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip7_bound.value.value <= 10'h8; + end else begin + if(field_combo.chip7_bound.value.load_next) begin + field_storage.chip7_bound.value.value <= field_combo.chip7_bound.value.next; + end + end + end + assign hwif_out.chip7_bound.value.value = field_storage.chip7_bound.value.value; //-------------------------------------------------------------------------- // Write response @@ -875,10 +1223,12 @@ module hyperbus_cfg_regblock ( readback_data_var[3:0] = field_storage.t_read_write_recovery.value.value; end if(rd_mux_addr == 7'h10) begin - readback_data_var[7:0] = field_storage.t_rx_clk_delay.value.value; + readback_data_var[4:0] = field_storage.t_rx_clk_delay.fine.value; + readback_data_var[7:5] = field_storage.t_rx_clk_delay.coarse.value; end if(rd_mux_addr == 7'h14) begin - readback_data_var[7:0] = field_storage.t_tx_clk_delay.value.value; + readback_data_var[4:0] = field_storage.t_tx_clk_delay.fine.value; + readback_data_var[7:5] = field_storage.t_tx_clk_delay.coarse.value; end if(rd_mux_addr == 7'h18) begin readback_data_var[4:0] = field_storage.address_mask_msb.value.value; @@ -899,28 +1249,52 @@ module hyperbus_cfg_regblock ( readback_data_var[3:0] = field_storage.csn_to_ck_cycles.value.value; end if(rd_mux_addr == 7'h30) begin - readback_data_var[31:0] = field_storage.chip0_base.value.value; + readback_data_var[31:22] = field_storage.chip0_base.value.value; end if(rd_mux_addr == 7'h34) begin - readback_data_var[31:0] = field_storage.chip0_bound.value.value; + readback_data_var[31:22] = field_storage.chip0_bound.value.value; end if(rd_mux_addr == 7'h38) begin - readback_data_var[31:0] = field_storage.chip1_base.value.value; + readback_data_var[31:22] = field_storage.chip1_base.value.value; end if(rd_mux_addr == 7'h3c) begin - readback_data_var[31:0] = field_storage.chip1_bound.value.value; + readback_data_var[31:22] = field_storage.chip1_bound.value.value; end if(rd_mux_addr == 7'h40) begin - readback_data_var[31:0] = field_storage.chip2_base.value.value; + readback_data_var[31:22] = field_storage.chip2_base.value.value; end if(rd_mux_addr == 7'h44) begin - readback_data_var[31:0] = field_storage.chip2_bound.value.value; + readback_data_var[31:22] = field_storage.chip2_bound.value.value; end if(rd_mux_addr == 7'h48) begin - readback_data_var[31:0] = field_storage.chip3_base.value.value; + readback_data_var[31:22] = field_storage.chip3_base.value.value; end if(rd_mux_addr == 7'h4c) begin - readback_data_var[31:0] = field_storage.chip3_bound.value.value; + readback_data_var[31:22] = field_storage.chip3_bound.value.value; + end + if(rd_mux_addr == 7'h58) begin + readback_data_var[31:22] = field_storage.chip4_base.value.value; + end + if(rd_mux_addr == 7'h5c) begin + readback_data_var[31:22] = field_storage.chip4_bound.value.value; + end + if(rd_mux_addr == 7'h60) begin + readback_data_var[31:22] = field_storage.chip5_base.value.value; + end + if(rd_mux_addr == 7'h64) begin + readback_data_var[31:22] = field_storage.chip5_bound.value.value; + end + if(rd_mux_addr == 7'h68) begin + readback_data_var[31:22] = field_storage.chip6_base.value.value; + end + if(rd_mux_addr == 7'h6c) begin + readback_data_var[31:22] = field_storage.chip6_bound.value.value; + end + if(rd_mux_addr == 7'h70) begin + readback_data_var[31:22] = field_storage.chip7_base.value.value; + end + if(rd_mux_addr == 7'h74) begin + readback_data_var[31:22] = field_storage.chip7_bound.value.value; end readback_data = readback_data_var; readback_done = decoded_req & ~decoded_req_is_wr; diff --git a/src/regs/hyperbus_cfg_regblock_pkg.sv b/src/regs/hyperbus_cfg_regblock_pkg.sv index 59572c7..85c5edb 100644 --- a/src/regs/hyperbus_cfg_regblock_pkg.sv +++ b/src/regs/hyperbus_cfg_regblock_pkg.sv @@ -5,7 +5,7 @@ package hyperbus_cfg_regblock_pkg; localparam HYPERBUS_CFG_REGBLOCK_DATA_WIDTH = 32; localparam HYPERBUS_CFG_REGBLOCK_MIN_ADDR_WIDTH = 7; - localparam HYPERBUS_CFG_REGBLOCK_SIZE = 'h50; + localparam HYPERBUS_CFG_REGBLOCK_SIZE = 'h78; typedef struct { logic [3:0] value; @@ -40,19 +40,29 @@ package hyperbus_cfg_regblock_pkg; } hyperbus_cfg_regs__t_read_write_recovery__out_t; typedef struct { - logic [7:0] value; - } hyperbus_cfg_regs__t_rx_clk_delay__value__out_t; + logic [4:0] value; + } hyperbus_cfg_regs__t_rx_clk_delay__fine__out_t; + + typedef struct { + logic [2:0] value; + } hyperbus_cfg_regs__t_rx_clk_delay__coarse__out_t; typedef struct { - hyperbus_cfg_regs__t_rx_clk_delay__value__out_t value; + hyperbus_cfg_regs__t_rx_clk_delay__fine__out_t fine; + hyperbus_cfg_regs__t_rx_clk_delay__coarse__out_t coarse; } hyperbus_cfg_regs__t_rx_clk_delay__out_t; typedef struct { - logic [7:0] value; - } hyperbus_cfg_regs__t_tx_clk_delay__value__out_t; + logic [4:0] value; + } hyperbus_cfg_regs__t_tx_clk_delay__fine__out_t; + + typedef struct { + logic [2:0] value; + } hyperbus_cfg_regs__t_tx_clk_delay__coarse__out_t; typedef struct { - hyperbus_cfg_regs__t_tx_clk_delay__value__out_t value; + hyperbus_cfg_regs__t_tx_clk_delay__fine__out_t fine; + hyperbus_cfg_regs__t_tx_clk_delay__coarse__out_t coarse; } hyperbus_cfg_regs__t_tx_clk_delay__out_t; typedef struct { @@ -104,7 +114,7 @@ package hyperbus_cfg_regblock_pkg; } hyperbus_cfg_regs__csn_to_ck_cycles__out_t; typedef struct { - logic [31:0] value; + logic [9:0] value; } hyperbus_cfg_regs__chip0_base__value__out_t; typedef struct { @@ -112,7 +122,7 @@ package hyperbus_cfg_regblock_pkg; } hyperbus_cfg_regs__chip0_base__out_t; typedef struct { - logic [31:0] value; + logic [9:0] value; } hyperbus_cfg_regs__chip0_bound__value__out_t; typedef struct { @@ -120,7 +130,7 @@ package hyperbus_cfg_regblock_pkg; } hyperbus_cfg_regs__chip0_bound__out_t; typedef struct { - logic [31:0] value; + logic [9:0] value; } hyperbus_cfg_regs__chip1_base__value__out_t; typedef struct { @@ -128,7 +138,7 @@ package hyperbus_cfg_regblock_pkg; } hyperbus_cfg_regs__chip1_base__out_t; typedef struct { - logic [31:0] value; + logic [9:0] value; } hyperbus_cfg_regs__chip1_bound__value__out_t; typedef struct { @@ -136,7 +146,7 @@ package hyperbus_cfg_regblock_pkg; } hyperbus_cfg_regs__chip1_bound__out_t; typedef struct { - logic [31:0] value; + logic [9:0] value; } hyperbus_cfg_regs__chip2_base__value__out_t; typedef struct { @@ -144,7 +154,7 @@ package hyperbus_cfg_regblock_pkg; } hyperbus_cfg_regs__chip2_base__out_t; typedef struct { - logic [31:0] value; + logic [9:0] value; } hyperbus_cfg_regs__chip2_bound__value__out_t; typedef struct { @@ -152,7 +162,7 @@ package hyperbus_cfg_regblock_pkg; } hyperbus_cfg_regs__chip2_bound__out_t; typedef struct { - logic [31:0] value; + logic [9:0] value; } hyperbus_cfg_regs__chip3_base__value__out_t; typedef struct { @@ -160,13 +170,77 @@ package hyperbus_cfg_regblock_pkg; } hyperbus_cfg_regs__chip3_base__out_t; typedef struct { - logic [31:0] value; + logic [9:0] value; } hyperbus_cfg_regs__chip3_bound__value__out_t; typedef struct { hyperbus_cfg_regs__chip3_bound__value__out_t value; } hyperbus_cfg_regs__chip3_bound__out_t; + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__chip4_base__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip4_base__value__out_t value; + } hyperbus_cfg_regs__chip4_base__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__chip4_bound__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip4_bound__value__out_t value; + } hyperbus_cfg_regs__chip4_bound__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__chip5_base__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip5_base__value__out_t value; + } hyperbus_cfg_regs__chip5_base__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__chip5_bound__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip5_bound__value__out_t value; + } hyperbus_cfg_regs__chip5_bound__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__chip6_base__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip6_base__value__out_t value; + } hyperbus_cfg_regs__chip6_base__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__chip6_bound__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip6_bound__value__out_t value; + } hyperbus_cfg_regs__chip6_bound__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__chip7_base__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip7_base__value__out_t value; + } hyperbus_cfg_regs__chip7_base__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__chip7_bound__value__out_t; + + typedef struct { + hyperbus_cfg_regs__chip7_bound__value__out_t value; + } hyperbus_cfg_regs__chip7_bound__out_t; + typedef struct { hyperbus_cfg_regs__t_latency_access__out_t t_latency_access; hyperbus_cfg_regs__en_latency_additional__out_t en_latency_additional; @@ -188,5 +262,13 @@ package hyperbus_cfg_regblock_pkg; hyperbus_cfg_regs__chip2_bound__out_t chip2_bound; hyperbus_cfg_regs__chip3_base__out_t chip3_base; hyperbus_cfg_regs__chip3_bound__out_t chip3_bound; + hyperbus_cfg_regs__chip4_base__out_t chip4_base; + hyperbus_cfg_regs__chip4_bound__out_t chip4_bound; + hyperbus_cfg_regs__chip5_base__out_t chip5_base; + hyperbus_cfg_regs__chip5_bound__out_t chip5_bound; + hyperbus_cfg_regs__chip6_base__out_t chip6_base; + hyperbus_cfg_regs__chip6_bound__out_t chip6_bound; + hyperbus_cfg_regs__chip7_base__out_t chip7_base; + hyperbus_cfg_regs__chip7_bound__out_t chip7_bound; } hyperbus_cfg_regs__out_t; endpackage diff --git a/src/regs/hyperbus_cfg_regs.rdl b/src/regs/hyperbus_cfg_regs.rdl index 5e49b7d..515e294 100644 --- a/src/regs/hyperbus_cfg_regs.rdl +++ b/src/regs/hyperbus_cfg_regs.rdl @@ -1,4 +1,6 @@ addrmap hyperbus_cfg_regs { + desc = "HyperBus controller configuration. Chip address ranges must be 4 MiB aligned, ordered by chip-select index, and non-overlapping."; + reg t_latency_access { desc = "Initial latency cycles before read or write data."; field { sw = rw; hw = r; reset = 4'h6; } value[3:0]; @@ -20,13 +22,35 @@ addrmap hyperbus_cfg_regs { }; reg t_rx_clk_delay { - desc = "RX sampling delay-line tap setting."; - field { sw = rw; hw = r; reset = 8'h10; } value[7:0]; + desc = "RX sampling delay with fine taps in bits 4:0 and coarse taps in bits 7:5."; + field { + desc = "Fine delay-line tap setting, nominally about 78 ps per tap."; + sw = rw; + hw = r; + reset = 5'h10; + } fine[4:0]; + field { + desc = "Coarse delay-line tap setting with an implementation-defined step size."; + sw = rw; + hw = r; + reset = 3'h0; + } coarse[7:5]; }; reg t_tx_clk_delay { - desc = "TX clock delay-line tap setting."; - field { sw = rw; hw = r; reset = 8'h10; } value[7:0]; + desc = "TX clock delay with fine taps in bits 4:0 and coarse taps in bits 7:5."; + field { + desc = "Fine delay-line tap setting, nominally about 78 ps per tap."; + sw = rw; + hw = r; + reset = 5'h10; + } fine[4:0]; + field { + desc = "Coarse delay-line tap setting with an implementation-defined step size."; + sw = rw; + hw = r; + reset = 3'h0; + } coarse[7:5]; }; reg address_mask_msb { @@ -60,43 +84,83 @@ addrmap hyperbus_cfg_regs { }; reg chip0_base { - desc = "Inclusive base address for chip-select 0 decoding."; - field { sw = rw; hw = r; reset = 32'h0000_0000; } value[31:0]; + desc = "Inclusive 4 MiB-aligned address for chip-select 0 decoding."; + field { sw = rw; hw = r; reset = 10'd0; } value[31:22]; }; reg chip0_bound { - desc = "Exclusive bound address for chip-select 0 decoding."; - field { sw = rw; hw = r; reset = 32'h0001_0000; } value[31:0]; + desc = "Exclusive 4 MiB-aligned address for chip-select 0 decoding."; + field { sw = rw; hw = r; reset = 10'd1; } value[31:22]; }; reg chip1_base { - desc = "Inclusive base address for chip-select 1 decoding."; - field { sw = rw; hw = r; reset = 32'h0001_0000; } value[31:0]; + desc = "Inclusive 4 MiB-aligned address for chip-select 1 decoding."; + field { sw = rw; hw = r; reset = 10'd1; } value[31:22]; }; reg chip1_bound { - desc = "Exclusive bound address for chip-select 1 decoding."; - field { sw = rw; hw = r; reset = 32'h0002_0000; } value[31:0]; + desc = "Exclusive 4 MiB-aligned address for chip-select 1 decoding."; + field { sw = rw; hw = r; reset = 10'd2; } value[31:22]; }; reg chip2_base { - desc = "Inclusive base address for chip-select 2 decoding."; - field { sw = rw; hw = r; reset = 32'h0002_0000; } value[31:0]; + desc = "Inclusive 4 MiB-aligned address for chip-select 2 decoding."; + field { sw = rw; hw = r; reset = 10'd2; } value[31:22]; }; reg chip2_bound { - desc = "Exclusive bound address for chip-select 2 decoding."; - field { sw = rw; hw = r; reset = 32'h0003_0000; } value[31:0]; + desc = "Exclusive 4 MiB-aligned address for chip-select 2 decoding."; + field { sw = rw; hw = r; reset = 10'd3; } value[31:22]; }; reg chip3_base { - desc = "Inclusive base address for chip-select 3 decoding."; - field { sw = rw; hw = r; reset = 32'h0003_0000; } value[31:0]; + desc = "Inclusive 4 MiB-aligned address for chip-select 3 decoding."; + field { sw = rw; hw = r; reset = 10'd3; } value[31:22]; }; reg chip3_bound { - desc = "Exclusive bound address for chip-select 3 decoding."; - field { sw = rw; hw = r; reset = 32'h0004_0000; } value[31:0]; + desc = "Exclusive 4 MiB-aligned address for chip-select 3 decoding."; + field { sw = rw; hw = r; reset = 10'd4; } value[31:22]; + }; + + reg chip4_base { + desc = "Inclusive 4 MiB-aligned address for chip-select 4 decoding."; + field { sw = rw; hw = r; reset = 10'd4; } value[31:22]; + }; + + reg chip4_bound { + desc = "Exclusive 4 MiB-aligned address for chip-select 4 decoding."; + field { sw = rw; hw = r; reset = 10'd5; } value[31:22]; + }; + + reg chip5_base { + desc = "Inclusive 4 MiB-aligned address for chip-select 5 decoding."; + field { sw = rw; hw = r; reset = 10'd5; } value[31:22]; + }; + + reg chip5_bound { + desc = "Exclusive 4 MiB-aligned address for chip-select 5 decoding."; + field { sw = rw; hw = r; reset = 10'd6; } value[31:22]; + }; + + reg chip6_base { + desc = "Inclusive 4 MiB-aligned address for chip-select 6 decoding."; + field { sw = rw; hw = r; reset = 10'd6; } value[31:22]; + }; + + reg chip6_bound { + desc = "Exclusive 4 MiB-aligned address for chip-select 6 decoding."; + field { sw = rw; hw = r; reset = 10'd7; } value[31:22]; + }; + + reg chip7_base { + desc = "Inclusive 4 MiB-aligned address for chip-select 7 decoding."; + field { sw = rw; hw = r; reset = 10'd7; } value[31:22]; + }; + + reg chip7_bound { + desc = "Exclusive 4 MiB-aligned address for chip-select 7 decoding."; + field { sw = rw; hw = r; reset = 10'd8; } value[31:22]; }; t_latency_access t_latency_access @ 0x00; @@ -119,4 +183,12 @@ addrmap hyperbus_cfg_regs { chip2_bound chip2_bound @ 0x44; chip3_base chip3_base @ 0x48; chip3_bound chip3_bound @ 0x4c; + chip4_base chip4_base @ 0x58; + chip4_bound chip4_bound @ 0x5c; + chip5_base chip5_base @ 0x60; + chip5_bound chip5_bound @ 0x64; + chip6_base chip6_base @ 0x68; + chip6_bound chip6_bound @ 0x6c; + chip7_base chip7_base @ 0x70; + chip7_bound chip7_bound @ 0x74; }; From 547802ae95a357b0673a4ac67d610f4e95988fce Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Tue, 21 Jul 2026 14:54:45 +0200 Subject: [PATCH 05/11] Simplify and test config register wrapper --- Bender.yml | 1 + scripts/start.tcl | 42 +++++++-- src/hyperbus_cfg_regs.sv | 56 ++++++++---- src/hyperbus_pkg.sv | 40 --------- test/hyperbus_cfg_regs_tb.sv | 165 +++++++++++++++++++++++++++++++++++ 5 files changed, 242 insertions(+), 62 deletions(-) create mode 100644 test/hyperbus_cfg_regs_tb.sv diff --git a/Bender.yml b/Bender.yml index 04062ce..b3bc15e 100644 --- a/Bender.yml +++ b/Bender.yml @@ -65,4 +65,5 @@ sources: - test/dut_if.sv - test/hyperbus_tb_pkg.sv - test/axi_hyper_tb.sv + - test/hyperbus_cfg_regs_tb.sv - src/hyperbus.sv diff --git a/scripts/start.tcl b/scripts/start.tcl index 0488812..f4408ad 100644 --- a/scripts/start.tcl +++ b/scripts/start.tcl @@ -2,12 +2,42 @@ # Solderpad Hardware License, Version 0.51, see LICENSE for details. # SPDX-License-Identifier: SHL-0.51 -vsim axi_hyper_tb -t 1ps -voptargs=+acc -classdebug +set regression_failed 0 -set StdArithNoWarnings 1 -set NumericStdNoWarnings 1 -log -r /* +proc run_test {top_name wlf_name} { + global regression_failed -delete wave * + puts "==============================" + puts "= Running $top_name" + puts "==============================" -run -all + set transcript_name "[file rootname $wlf_name].log" + file delete -force $transcript_name + transcript file $transcript_name + + vsim $top_name -wlf $wlf_name -t 1ps -voptargs=+acc -classdebug + + onfinish stop + set StdArithNoWarnings 1 + set NumericStdNoWarnings 1 + log -r /* + + catch {delete wave *} + + run -all + quit -sim + + transcript file {} + set transcript_fd [open $transcript_name r] + set transcript_data [read $transcript_fd] + close $transcript_fd + if {[regexp -line {^# \*\* (Error|Fatal)( \([^)]*\))?:} $transcript_data]} { + puts "Regression errors detected in $transcript_name" + set regression_failed 1 + } +} + +run_test axi_hyper_tb sim_run_axi.wlf +run_test hyperbus_cfg_regs_tb sim_run_cfg_regs.wlf + +quit -code $regression_failed -f diff --git a/src/hyperbus_cfg_regs.sv b/src/hyperbus_cfg_regs.sv index 84e792f..6c6ebd1 100644 --- a/src/hyperbus_cfg_regs.sv +++ b/src/hyperbus_cfg_regs.sv @@ -4,6 +4,8 @@ // // Paul Scheffler +`include "common_cells/assertions.svh" + module hyperbus_cfg_regs #( parameter int unsigned NumChips = -1, parameter int unsigned NumPhys = -1, @@ -12,8 +14,8 @@ module hyperbus_cfg_regs #( parameter type reg_rsp_t = logic, parameter type rule_t = logic ) ( - input logic clk_i, - input logic rst_ni, + input logic clk_i, + input logic rst_ni, input reg_req_t reg_req_i, output reg_rsp_t reg_rsp_o, @@ -30,8 +32,11 @@ module hyperbus_cfg_regs #( localparam int unsigned RegsBits = cf_math_pkg::idx_width(NumRegs); localparam int unsigned RegStrbWidth = RegDataWidth/8; + `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= NumChipsMax) + `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) + `ASSERT_INIT(RegDataWidthValid, RegDataWidth == 32) + typedef logic [RegsBits-1:0] reg_idx_t; - typedef logic [RegDataWidth-1:0] reg_data_t; typedef logic [6:0] cfg_addr_t; typedef logic [31:0] cfg_data_t; typedef logic [3:0] cfg_strb_t; @@ -94,7 +99,7 @@ module hyperbus_cfg_regs #( assign cfg_reg_req.wdata = 32'(reg_req_i.wdata); assign cfg_reg_req.wstrb = cfg_strb_t'(reg_req_i.wstrb); - always_comb begin + always_comb begin : proc_cfg_access cfg_access_active_d = cfg_access_active_q; if (!cfg_access_active_q && cfg_reg_req.valid) begin cfg_access_active_d = 1'b1; @@ -106,7 +111,7 @@ module hyperbus_cfg_regs #( `FFARN(cfg_access_active_q, cfg_access_active_d, 1'b0, clk_i, rst_ni); - always_comb begin + always_comb begin : proc_chip_rules chip_rules_all = '0; for (int unsigned i = 0; i < NumChipsMax; i++) begin chip_rules_all[i].idx = unsigned'(i); @@ -194,8 +199,36 @@ module hyperbus_cfg_regs #( .hwif_out ( cfg_hwif_out ) ); - assign frontend_cfg_o = hyperbus_pkg::hwif_to_frontend_cfg(cfg_hwif_out, NumPhys == 1); - assign phy_cfg_o = hyperbus_pkg::hwif_to_phy_cfg(cfg_hwif_out, NumPhys == 1); + always_comb begin : proc_cfg_output + frontend_cfg_o = '0; + phy_cfg_o = '0; + + frontend_cfg_o.address_mask_msb = cfg_hwif_out.address_mask_msb.value.value; + frontend_cfg_o.address_space = cfg_hwif_out.address_space.value.value; + frontend_cfg_o.phys_in_use = (NumPhys == 2) && + cfg_hwif_out.phys_in_use.value.value; + frontend_cfg_o.which_phy = (NumPhys == 2) && + cfg_hwif_out.which_phy.value.value; + + phy_cfg_o.chip.t_latency_access = cfg_hwif_out.t_latency_access.value.value; + phy_cfg_o.chip.en_latency_additional = cfg_hwif_out.en_latency_additional.value.value; + phy_cfg_o.chip.t_burst_max = cfg_hwif_out.t_burst_max.value.value; + phy_cfg_o.chip.t_read_write_recovery = cfg_hwif_out.t_read_write_recovery.value.value; + phy_cfg_o.chip.t_rx_clk_delay = { + cfg_hwif_out.t_rx_clk_delay.coarse.value, + cfg_hwif_out.t_rx_clk_delay.fine.value + }; + phy_cfg_o.chip.t_csh_cycles = cfg_hwif_out.t_csh_cycles.value.value; + phy_cfg_o.chip.csn_to_ck_cycles = cfg_hwif_out.csn_to_ck_cycles.value.value; + phy_cfg_o.t_tx_clk_delay = { + cfg_hwif_out.t_tx_clk_delay.coarse.value, + cfg_hwif_out.t_tx_clk_delay.fine.value + }; + phy_cfg_o.phys_in_use = (NumPhys == 2) && + cfg_hwif_out.phys_in_use.value.value; + phy_cfg_o.which_phy = (NumPhys == 2) && + cfg_hwif_out.which_phy.value.value; + end for (genvar i = 0; unsigned'(i) < NumChipsMax; i++) begin : gen_chip_rules if (i < NumChips) begin : gen_active @@ -206,13 +239,4 @@ module hyperbus_cfg_regs #( end end - // pragma translate_off - `ifndef VERILATOR - initial assert (RegDataWidth == 32) - else $error("Generated HyperBus config registers require 32-bit RegDataWidth."); - initial assert (NumChips <= NumChipsMax) - else $error("Generated HyperBus config registers support up to eight chips."); - `endif - // pragma translate_on - endmodule : hyperbus_cfg_regs diff --git a/src/hyperbus_pkg.sv b/src/hyperbus_pkg.sv index 1fa6219..49149ef 100644 --- a/src/hyperbus_pkg.sv +++ b/src/hyperbus_pkg.sv @@ -33,46 +33,6 @@ package hyperbus_pkg; logic which_phy; } phy_cfg_t; - function automatic frontend_cfg_t hwif_to_frontend_cfg( - input hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t hwif, - input logic num_phys_one - ); - frontend_cfg_t cfg; - - cfg.address_mask_msb = hwif.address_mask_msb.value.value; - cfg.address_space = hwif.address_space.value.value; - cfg.phys_in_use = num_phys_one ? 1'b0 : hwif.phys_in_use.value.value; - cfg.which_phy = num_phys_one ? 1'b0 : hwif.which_phy.value.value; - - return cfg; - endfunction - - function automatic phy_cfg_t hwif_to_phy_cfg( - input hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t hwif, - input logic num_phys_one - ); - phy_cfg_t cfg; - - cfg.chip.t_latency_access = hwif.t_latency_access.value.value; - cfg.chip.en_latency_additional = hwif.en_latency_additional.value.value; - cfg.chip.t_burst_max = hwif.t_burst_max.value.value; - cfg.chip.t_read_write_recovery = hwif.t_read_write_recovery.value.value; - cfg.chip.t_rx_clk_delay = { - hwif.t_rx_clk_delay.coarse.value, - hwif.t_rx_clk_delay.fine.value - }; - cfg.chip.t_csh_cycles = hwif.t_csh_cycles.value.value; - cfg.chip.csn_to_ck_cycles = hwif.csn_to_ck_cycles.value.value; - cfg.t_tx_clk_delay = { - hwif.t_tx_clk_delay.coarse.value, - hwif.t_tx_clk_delay.fine.value - }; - cfg.phys_in_use = num_phys_one ? 1'b0 : hwif.phys_in_use.value.value; - cfg.which_phy = num_phys_one ? 1'b0 : hwif.which_phy.value.value; - - return cfg; - endfunction - typedef struct packed { logic write; // transaction is a write hyper_blen_t burst; diff --git a/test/hyperbus_cfg_regs_tb.sv b/test/hyperbus_cfg_regs_tb.sv new file mode 100644 index 0000000..2209dcb --- /dev/null +++ b/test/hyperbus_cfg_regs_tb.sv @@ -0,0 +1,165 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +module hyperbus_cfg_regs_tb; + + localparam int unsigned NumChips = 8; + + typedef struct packed { + logic [7:0] addr; + logic write; + logic [31:0] wdata; + logic [3:0] wstrb; + logic valid; + } reg_req_t; + + typedef struct packed { + logic [31:0] rdata; + logic error; + logic ready; + } reg_rsp_t; + + typedef struct packed { + logic [31:0] start_addr; + logic [31:0] end_addr; + logic [2:0] idx; + } rule_t; + + logic clk; + logic rst_n; + reg_req_t reg_req; + reg_rsp_t reg_rsp; + hyperbus_pkg::frontend_cfg_t frontend_cfg; + hyperbus_pkg::phy_cfg_t phy_cfg; + rule_t [NumChips-1:0] chip_rules; + logic trans_active; + + task automatic reg_write( + input logic [7:0] addr, + input logic [31:0] data + ); + @(negedge clk); + reg_req.addr = addr; + reg_req.write = 1'b1; + reg_req.wdata = data; + reg_req.wstrb = '1; + reg_req.valid = 1'b1; + + do begin + @(posedge clk); + end while (!reg_rsp.ready); + if (reg_rsp.error) begin + $error("Register write to 0x%0h failed", addr); + end + + @(negedge clk); + reg_req.valid = 1'b0; + endtask + + task automatic reg_read( + input logic [7:0] addr, + output logic [31:0] data + ); + @(negedge clk); + reg_req.addr = addr; + reg_req.write = 1'b0; + reg_req.wdata = '0; + reg_req.wstrb = '0; + reg_req.valid = 1'b1; + + do begin + @(posedge clk); + end while (!reg_rsp.ready); + data = reg_rsp.rdata; + if (reg_rsp.error) begin + $error("Register read from 0x%0h failed", addr); + end + + @(negedge clk); + reg_req.valid = 1'b0; + endtask + + always #5ns clk = ~clk; + + hyperbus_cfg_regs #( + .NumChips ( NumChips ), + .NumPhys ( 2 ), + .RegDataWidth ( 32 ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .rule_t ( rule_t ) + ) i_dut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .reg_req_i ( reg_req ), + .reg_rsp_o ( reg_rsp ), + .frontend_cfg_o ( frontend_cfg ), + .phy_cfg_o ( phy_cfg ), + .chip_rules_o ( chip_rules ), + .trans_active_i ( trans_active ) + ); + + initial begin : proc_stimulus + logic [31:0] chip_addr; + logic [31:0] rx_clk_delay; + logic [31:0] tx_clk_delay; + + clk = 1'b0; + rst_n = 1'b0; + reg_req = '0; + trans_active = 1'b0; + + repeat (4) @(posedge clk); + rst_n = 1'b1; + repeat (2) @(posedge clk); + + for (int unsigned i = 0; i < NumChips; i++) begin + if ((chip_rules[i].start_addr != i * 32'h0040_0000) || + (chip_rules[i].end_addr != (i + 1) * 32'h0040_0000)) begin + $error("Unexpected reset address range for chip %0d", i); + end + end + + // Exercise both the fine [4:0] and coarse [7:5] delay settings. + reg_write(8'h10, 8'hb5); + reg_write(8'h14, 8'h6a); + reg_read(8'h10, rx_clk_delay); + reg_read(8'h14, tx_clk_delay); + if ((rx_clk_delay != 8'hb5) || (tx_clk_delay != 8'h6a) || + (phy_cfg.chip.t_rx_clk_delay != 8'hb5) || + (phy_cfg.t_tx_clk_delay != 8'h6a)) begin + $error("Delay-line configuration fields did not take effect"); + end + + // Low address bits are not stored and read back as zero. + reg_write(8'h70, 32'h8134_5678); + reg_read(8'h70, chip_addr); + if ((chip_addr != 32'h8100_0000) || + (chip_rules[7].start_addr != 32'h8100_0000)) begin + $error("Chip address alignment was not applied"); + end + + // A new configuration access must wait for an active transfer to finish. + trans_active = 1'b1; + @(negedge clk); + reg_req.addr = 8'h00; + reg_req.write = 1'b0; + reg_req.valid = 1'b1; + repeat (3) begin + @(posedge clk); + if (reg_rsp.ready) begin + $error("Configuration access completed during an active transfer"); + end + end + trans_active = 1'b0; + do begin + @(posedge clk); + end while (!reg_rsp.ready); + @(negedge clk); + reg_req.valid = 1'b0; + + $finish; + end + +endmodule From 0925d1139964f962cc87504fe44c6e177bf2963d Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Thu, 23 Jul 2026 17:18:54 +0200 Subject: [PATCH 06/11] Split HyperBus into clocking variants Introduce asynchronous, isochronous, and synchronous top levels around a shared backend architecture. Extend the regression wrapper so each clocking arrangement is exercised. --- .gitignore | 3 + Bender.yml | 10 +- scripts/start.tcl | 7 +- src/hyperbus.sv | 304 ----------------------------------- src/hyperbus_async_bridge.sv | 129 +++++++++++++++ src/hyperbus_asynchronous.sv | 269 +++++++++++++++++++++++++++++++ src/hyperbus_axi.sv | 3 +- src/hyperbus_backend.sv | 106 ++++++++++++ src/hyperbus_frontend.sv | 176 ++++++++++++++++++++ src/hyperbus_iso_bridge.sv | 226 ++++++++++++++++++++++++++ src/hyperbus_isochronous.sv | 261 ++++++++++++++++++++++++++++++ src/hyperbus_phy.sv | 29 +++- src/hyperbus_phy2r.sv | 14 +- src/hyperbus_phy_if.sv | 36 +---- src/hyperbus_stub.sv | 57 ------- src/hyperbus_synchronous.sv | 194 ++++++++++++++++++++++ src/hyperbus_synth_wrap.sv | 231 -------------------------- src/hyperbus_tx_clk_delay.sv | 33 ++++ test/axi_hyper_tb.sv | 207 ++++++++++++++++++++++-- test/dut_if.sv | 48 +++++- test/fixture_hyperbus.sv | 23 +-- test/hyperbus_test_dut.sv | 195 ++++++++++++++++++++++ 22 files changed, 1895 insertions(+), 666 deletions(-) delete mode 100644 src/hyperbus.sv create mode 100644 src/hyperbus_async_bridge.sv create mode 100644 src/hyperbus_asynchronous.sv create mode 100644 src/hyperbus_backend.sv create mode 100644 src/hyperbus_frontend.sv create mode 100644 src/hyperbus_iso_bridge.sv create mode 100644 src/hyperbus_isochronous.sv delete mode 100644 src/hyperbus_stub.sv create mode 100644 src/hyperbus_synchronous.sv delete mode 100644 src/hyperbus_synth_wrap.sv create mode 100644 src/hyperbus_tx_clk_delay.sv create mode 100644 test/hyperbus_test_dut.sv diff --git a/.gitignore b/.gitignore index a63d836..e710fc7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ work/ transcript modelsim.ini vsim.wlf +sim_run*.wlf +sim_*.log models/s27ks0641 +wlft* diff --git a/Bender.yml b/Bender.yml index b3bc15e..cb14edc 100644 --- a/Bender.yml +++ b/Bender.yml @@ -42,6 +42,7 @@ sources: - src/hyperbus_delay.sv - src/regs/hyperbus_cfg_regblock_pkg.sv + - src/hyperbus_tx_clk_delay.sv - src/hyperbus_pkg.sv - src/hyperbus_clk_gen.sv - src/hyperbus_clock_diff_out.sv @@ -54,16 +55,23 @@ sources: - src/hyperbus_phy.sv - src/hyperbus_phy_if.sv - src/hyperbus_axi.sv + - src/hyperbus_frontend.sv + - src/hyperbus_async_bridge.sv + - src/hyperbus_iso_bridge.sv + - src/hyperbus_backend.sv + - src/hyperbus_synchronous.sv + - src/hyperbus_asynchronous.sv + - src/hyperbus_isochronous.sv - target: hyper_test files: # Device models. TODO: extend - models/s27ks0641/s27ks0641.v # Testbench + - test/hyperbus_test_dut.sv - test/fixture_hyperbus.sv - test/hyperbus_tb.sv - test/dut_if.sv - test/hyperbus_tb_pkg.sv - test/axi_hyper_tb.sv - test/hyperbus_cfg_regs_tb.sv - - src/hyperbus.sv diff --git a/scripts/start.tcl b/scripts/start.tcl index f4408ad..92aeeb9 100644 --- a/scripts/start.tcl +++ b/scripts/start.tcl @@ -21,7 +21,6 @@ proc run_test {top_name wlf_name} { set StdArithNoWarnings 1 set NumericStdNoWarnings 1 log -r /* - catch {delete wave *} run -all @@ -37,7 +36,9 @@ proc run_test {top_name wlf_name} { } } -run_test axi_hyper_tb sim_run_axi.wlf -run_test hyperbus_cfg_regs_tb sim_run_cfg_regs.wlf +run_test axi_hyper_tb_isochronous sim_run_isochronous.wlf +run_test axi_hyper_tb_synchronous sim_run_synchronous.wlf +run_test axi_hyper_tb_asynchronous sim_run_asynchronous.wlf +run_test hyperbus_cfg_regs_tb sim_run_cfg_regs.wlf quit -code $regression_failed -f diff --git a/src/hyperbus.sv b/src/hyperbus.sv deleted file mode 100644 index ff0faa6..0000000 --- a/src/hyperbus.sv +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright 2023 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 -// -// Thomas Benz -// Paul Scheffler -// Luca Valente - -module hyperbus #( - parameter int unsigned NumChips = -1, - parameter int unsigned NumPhys = 2, - parameter bit UsePhyClkDivider = 1, - parameter int unsigned AxiAddrWidth = -1, - parameter int unsigned AxiDataWidth = -1, - parameter int unsigned AxiIdWidth = -1, - parameter int unsigned AxiUserWidth = -1, - parameter type axi_req_t = logic, - parameter type axi_rsp_t = logic, - parameter type axi_w_chan_t = logic, - parameter type axi_b_chan_t = logic, - parameter type axi_ar_chan_t = logic, - parameter type axi_r_chan_t = logic, - parameter type axi_aw_chan_t = logic, - parameter int unsigned RegDataWidth = -1, - parameter type reg_req_t = logic, - parameter type reg_rsp_t = logic, - parameter type axi_rule_t = logic, - // The below have sensible defaults, but should be set on integration! - parameter int unsigned RxFifoLogDepth = 3, - parameter int unsigned TxFifoLogDepth = 3, - parameter int unsigned PhyStartupCycles = 300 * 200, /* us*MHz */ // Conservative maximum frequency estimate - parameter int unsigned SyncStages = 2 -) ( - input logic clk_phy_i, -`ifdef TARGET_XILINX - input logic clk_ref200_i, // only used for Xilinx delay lines -`endif - input logic rst_phy_ni, - input logic clk_sys_i, - input logic rst_sys_ni, - input logic test_mode_i, - // AXI bus - input axi_req_t axi_req_i, - output axi_rsp_t axi_rsp_o, - // Reg bus - input reg_req_t reg_req_i, - output reg_rsp_t reg_rsp_o, - // Physical interace: facing HyperBus PADs - output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, - output logic [NumPhys-1:0] hyper_ck_o, - output logic [NumPhys-1:0] hyper_ck_no, - output logic [NumPhys-1:0] hyper_rwds_o, - input logic [NumPhys-1:0] hyper_rwds_i, - output logic [NumPhys-1:0] hyper_rwds_oe_o, - input logic [NumPhys-1:0][7:0] hyper_dq_i, - output logic [NumPhys-1:0][7:0] hyper_dq_o, - output logic [NumPhys-1:0] hyper_dq_oe_o, - output logic [NumPhys-1:0] hyper_reset_no -); - - typedef struct packed { - logic [(16*NumPhys)-1:0] data; - logic last; - logic [(2*NumPhys)-1:0] strb; // mask data - } hyper_tx_t; - - typedef struct packed { - logic [(16*NumPhys)-1:0] data; - logic last; - logic error; - } hyper_rx_t; - - // Combined transfer type for CDC - typedef struct packed { - hyperbus_pkg::hyper_tf_t trans; - logic [NumChips-1:0] cs; - } tf_cdc_t; - - - logic clk_phy_0, clk_phy_90, rst_phy; - - // Register file - hyperbus_pkg::frontend_cfg_t frontend_cfg; - hyperbus_pkg::phy_cfg_t phy_cfg; - axi_rule_t [NumChips-1:0] chip_rules; - logic trans_active; - - // AXI slave - hyper_rx_t axi_rx; - logic axi_rx_valid; - logic axi_rx_ready; - hyper_tx_t axi_tx; - logic axi_tx_valid; - logic axi_tx_ready; - logic axi_b_error; - logic axi_b_valid; - logic axi_b_ready; - tf_cdc_t axi_tf_cdc; - logic axi_trans_valid; - logic axi_trans_ready; - - // PHY - hyper_rx_t phy_rx; - logic phy_rx_valid; - logic phy_rx_ready; - hyper_tx_t phy_tx; - logic phy_tx_valid; - logic phy_tx_ready; - logic phy_b_error; - logic phy_b_valid; - logic phy_b_ready; - tf_cdc_t phy_tf_cdc; - logic phy_trans_valid; - logic phy_trans_ready; - - // Config register File - hyperbus_cfg_regs #( - .NumChips ( NumChips ), - .NumPhys ( NumPhys ), - .RegDataWidth ( RegDataWidth ), - .reg_req_t ( reg_req_t ), - .reg_rsp_t ( reg_rsp_t ), - .rule_t ( axi_rule_t ) - ) i_cfg_regs ( - .clk_i ( clk_sys_i ), - .rst_ni ( rst_sys_ni ), - .reg_req_i ( reg_req_i ), - .reg_rsp_o ( reg_rsp_o ), - .frontend_cfg_o ( frontend_cfg ), - .phy_cfg_o ( phy_cfg ), - .chip_rules_o ( chip_rules ), - .trans_active_i ( trans_active ) - ); - - // AXI slave interfacing PHY - hyperbus_axi #( - .AxiDataWidth ( AxiDataWidth ), - .AxiAddrWidth ( AxiAddrWidth ), - .AxiIdWidth ( AxiIdWidth ), - .AxiUserWidth ( AxiUserWidth ), - .axi_req_t ( axi_req_t ), - .axi_rsp_t ( axi_rsp_t ), - .NumChips ( NumChips ), - .NumPhys ( NumPhys ), - .hyper_rx_t ( hyper_rx_t ), - .hyper_tx_t ( hyper_tx_t ), - .rule_t ( axi_rule_t ) - ) i_axi_slave ( - .clk_i ( clk_sys_i ), - .rst_ni ( rst_sys_ni ), - - .axi_req_i ( axi_req_i ), - .axi_rsp_o ( axi_rsp_o ), - - .rx_i ( axi_rx ), - .rx_valid_i ( axi_rx_valid ), - .rx_ready_o ( axi_rx_ready ), - .tx_o ( axi_tx ), - .tx_valid_o ( axi_tx_valid ), - .tx_ready_i ( axi_tx_ready ), - .b_error_i ( axi_b_error ), - .b_valid_i ( axi_b_valid ), - .b_ready_o ( axi_b_ready ), - .trans_o ( axi_tf_cdc.trans ), - .trans_cs_o ( axi_tf_cdc.cs ), - .trans_valid_o ( axi_trans_valid ), - .trans_ready_i ( axi_trans_ready ), - - .frontend_cfg_i ( frontend_cfg ), - .chip_rules_i ( chip_rules ), - .trans_active_o ( trans_active ) - ); - - if(UsePhyClkDivider == 1'b1) begin : clock_generator - hyperbus_clk_gen ddr_clk ( - .clk_i ( clk_phy_i ), - .rst_ni ( rst_phy_ni ), - .clk0_o ( clk_phy_0 ), - .clk90_o ( clk_phy_90 ), - .clk180_o ( ), - .clk270_o ( ), - .rst_no ( rst_phy ) - ); - end else begin - assign clk_phy_0 = clk_phy_i; - assign clk_phy_90 = '0; - assign rst_phy = rst_phy_ni; - end - - hyperbus_phy_if #( - .UsePhyClkDivider ( UsePhyClkDivider ), - .NumChips ( NumChips ), - .NumPhys ( NumPhys ), - .StartupCycles ( PhyStartupCycles ), - .hyper_rx_t ( hyper_rx_t ), - .hyper_tx_t ( hyper_tx_t ), - .SyncStages ( SyncStages ) - ) i_phy ( - .clk_phy_i ( clk_phy_0 ), - .clk_phy_i_90 ( clk_phy_90 ), -`ifdef TARGET_XILINX - .clk_ref200_i ( clk_ref200_i ), -`endif - .rst_phy_ni ( rst_phy ), - .test_mode_i ( test_mode_i ), - - .cfg_i ( phy_cfg ), - - .rx_o ( phy_rx ), - .rx_valid_o ( phy_rx_valid ), - .rx_ready_i ( phy_rx_ready ), - .tx_i ( phy_tx ), - .tx_valid_i ( phy_tx_valid ), - .tx_ready_o ( phy_tx_ready ), - .b_error_o ( phy_b_error ), - .b_valid_o ( phy_b_valid ), - .b_ready_i ( phy_b_ready ), - .trans_i ( phy_tf_cdc.trans ), - .trans_cs_i ( phy_tf_cdc.cs ), - .trans_valid_i ( phy_trans_valid ), - .trans_ready_o ( phy_trans_ready ), - - .hyper_cs_no ( hyper_cs_no ), - .hyper_ck_o ( hyper_ck_o ), - .hyper_ck_no ( hyper_ck_no ), - .hyper_rwds_o ( hyper_rwds_o ), - .hyper_rwds_i ( hyper_rwds_i ), - .hyper_rwds_oe_o( hyper_rwds_oe_o ), - .hyper_dq_i ( hyper_dq_i ), - .hyper_dq_o ( hyper_dq_o ), - .hyper_dq_oe_o ( hyper_dq_oe_o ), - .hyper_reset_no ( hyper_reset_no ) - ); - - cdc_2phase #( - .T ( tf_cdc_t ) - ) i_cdc_2phase_trans ( - .src_rst_ni ( rst_sys_ni ), - .src_clk_i ( clk_sys_i ), - .src_data_i ( axi_tf_cdc ), - .src_valid_i ( axi_trans_valid ), - .src_ready_o ( axi_trans_ready ), - - .dst_rst_ni ( rst_phy ), - .dst_clk_i ( clk_phy_0 ), - .dst_data_o ( phy_tf_cdc ), - .dst_valid_o ( phy_trans_valid ), - .dst_ready_i ( phy_trans_ready ) - ); - - cdc_2phase #( - .T ( logic ) - ) i_cdc_2phase_b ( - .src_rst_ni ( rst_phy ), - .src_clk_i ( clk_phy_0 ), - .src_data_i ( phy_b_error ), - .src_valid_i ( phy_b_valid ), - .src_ready_o ( phy_b_ready ), - - .dst_rst_ni ( rst_sys_ni ), - .dst_clk_i ( clk_sys_i ), - .dst_data_o ( axi_b_error ), - .dst_valid_o ( axi_b_valid ), - .dst_ready_i ( axi_b_ready ) - ); - - // Write data, TX CDC FIFO - cdc_fifo_gray #( - .T ( hyper_tx_t ), - .LOG_DEPTH ( TxFifoLogDepth ) - ) i_cdc_fifo_tx ( - .src_rst_ni ( rst_sys_ni ), - .src_clk_i ( clk_sys_i ), - .src_data_i ( axi_tx ), - .src_valid_i ( axi_tx_valid ), - .src_ready_o ( axi_tx_ready ), - - .dst_rst_ni ( rst_phy ), - .dst_clk_i ( clk_phy_0 ), - .dst_data_o ( phy_tx ), - .dst_valid_o ( phy_tx_valid ), - .dst_ready_i ( phy_tx_ready ) - ); - - // Read data, RX CDC FIFO - cdc_fifo_gray #( - .T ( hyper_rx_t ), - .LOG_DEPTH ( RxFifoLogDepth ) - ) i_cdc_fifo_rx ( - .src_rst_ni ( rst_phy ), - .src_clk_i ( clk_phy_0 ), - .src_data_i ( phy_rx ), - .src_valid_i ( phy_rx_valid ), - .src_ready_o ( phy_rx_ready ), - - .dst_rst_ni ( rst_sys_ni ), - .dst_clk_i ( clk_sys_i ), - .dst_data_o ( axi_rx ), - .dst_valid_o ( axi_rx_valid ), - .dst_ready_i ( axi_rx_ready ) - ); - - -endmodule : hyperbus diff --git a/src/hyperbus_async_bridge.sv b/src/hyperbus_async_bridge.sv new file mode 100644 index 0000000..120cec3 --- /dev/null +++ b/src/hyperbus_async_bridge.sv @@ -0,0 +1,129 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +module hyperbus_async_bridge #( + parameter int unsigned RxFifoLogDepth = 3, + parameter int unsigned TxFifoLogDepth = 3, + parameter type hyper_rx_t = logic, + parameter type hyper_tx_t = logic, + parameter type tf_cdc_t = logic +) ( + input logic clk_sys_i, + input logic rst_sys_ni, + input logic clk_phy_i, + input logic rst_phy_ni, + + input hyperbus_pkg::hyper_cfg_t cfg_apply_i, + input logic frontend_cfg_apply_valid_i, + output logic frontend_cfg_apply_ready_o, + + output hyper_rx_t frontend_rx_o, + output logic frontend_rx_valid_o, + input logic frontend_rx_ready_i, + input hyper_tx_t frontend_tx_i, + input logic frontend_tx_valid_i, + output logic frontend_tx_ready_o, + output logic frontend_b_error_o, + output logic frontend_b_valid_o, + input logic frontend_b_ready_i, + input tf_cdc_t frontend_trans_i, + input logic frontend_trans_valid_i, + output logic frontend_trans_ready_o, + + input hyper_rx_t backend_rx_i, + input logic backend_rx_valid_i, + output logic backend_rx_ready_o, + output hyper_tx_t backend_tx_o, + output logic backend_tx_valid_o, + input logic backend_tx_ready_i, + input logic backend_b_error_i, + input logic backend_b_valid_i, + output logic backend_b_ready_o, + output tf_cdc_t backend_trans_o, + output logic backend_trans_valid_o, + input logic backend_trans_ready_i, + + output hyperbus_pkg::hyper_cfg_t cfg_apply_o, + output logic cfg_apply_valid_o, + input logic cfg_apply_ready_i +); + + cdc_2phase #( + .T ( hyperbus_pkg::hyper_cfg_t ) + ) i_cdc_2phase_cfg ( + .src_rst_ni ( rst_sys_ni ), + .src_clk_i ( clk_sys_i ), + .src_data_i ( cfg_apply_i ), + .src_valid_i ( frontend_cfg_apply_valid_i ), + .src_ready_o ( frontend_cfg_apply_ready_o ), + .dst_rst_ni ( rst_phy_ni ), + .dst_clk_i ( clk_phy_i ), + .dst_data_o ( cfg_apply_o ), + .dst_valid_o ( cfg_apply_valid_o ), + .dst_ready_i ( cfg_apply_ready_i ) + ); + + cdc_2phase #( + .T ( tf_cdc_t ) + ) i_cdc_2phase_trans ( + .src_rst_ni ( rst_sys_ni ), + .src_clk_i ( clk_sys_i ), + .src_data_i ( frontend_trans_i ), + .src_valid_i ( frontend_trans_valid_i ), + .src_ready_o ( frontend_trans_ready_o ), + .dst_rst_ni ( rst_phy_ni ), + .dst_clk_i ( clk_phy_i ), + .dst_data_o ( backend_trans_o ), + .dst_valid_o ( backend_trans_valid_o ), + .dst_ready_i ( backend_trans_ready_i ) + ); + + cdc_2phase #( + .T ( logic ) + ) i_cdc_2phase_b ( + .src_rst_ni ( rst_phy_ni ), + .src_clk_i ( clk_phy_i ), + .src_data_i ( backend_b_error_i ), + .src_valid_i ( backend_b_valid_i ), + .src_ready_o ( backend_b_ready_o ), + .dst_rst_ni ( rst_sys_ni ), + .dst_clk_i ( clk_sys_i ), + .dst_data_o ( frontend_b_error_o ), + .dst_valid_o ( frontend_b_valid_o ), + .dst_ready_i ( frontend_b_ready_i ) + ); + + cdc_fifo_gray #( + .T ( hyper_tx_t ), + .LOG_DEPTH ( TxFifoLogDepth ) + ) i_cdc_fifo_tx ( + .src_rst_ni ( rst_sys_ni ), + .src_clk_i ( clk_sys_i ), + .src_data_i ( frontend_tx_i ), + .src_valid_i ( frontend_tx_valid_i ), + .src_ready_o ( frontend_tx_ready_o ), + .dst_rst_ni ( rst_phy_ni ), + .dst_clk_i ( clk_phy_i ), + .dst_data_o ( backend_tx_o ), + .dst_valid_o ( backend_tx_valid_o ), + .dst_ready_i ( backend_tx_ready_i ) + ); + + cdc_fifo_gray #( + .T ( hyper_rx_t ), + .LOG_DEPTH ( RxFifoLogDepth ) + ) i_cdc_fifo_rx ( + .src_rst_ni ( rst_phy_ni ), + .src_clk_i ( clk_phy_i ), + .src_data_i ( backend_rx_i ), + .src_valid_i ( backend_rx_valid_i ), + .src_ready_o ( backend_rx_ready_o ), + .dst_rst_ni ( rst_sys_ni ), + .dst_clk_i ( clk_sys_i ), + .dst_data_o ( frontend_rx_o ), + .dst_valid_o ( frontend_rx_valid_o ), + .dst_ready_i ( frontend_rx_ready_i ) + ); + +endmodule diff --git a/src/hyperbus_asynchronous.sv b/src/hyperbus_asynchronous.sv new file mode 100644 index 0000000..3e5c763 --- /dev/null +++ b/src/hyperbus_asynchronous.sv @@ -0,0 +1,269 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +module hyperbus_asynchronous #( + parameter int unsigned NumChips = -1, + parameter int unsigned NumPhys = 2, + parameter int unsigned AxiAddrWidth = -1, + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned AxiIdWidth = -1, + parameter int unsigned AxiUserWidth = -1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter type axi_w_chan_t = logic, + parameter type axi_b_chan_t = logic, + parameter type axi_ar_chan_t = logic, + parameter type axi_r_chan_t = logic, + parameter type axi_aw_chan_t = logic, + parameter int unsigned RegAddrWidth = -1, + parameter int unsigned RegDataWidth = -1, + parameter int unsigned MinFreqMHz = 100, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type axi_rule_t = logic, + parameter int unsigned RxFifoLogDepth = 3, + parameter int unsigned TxFifoLogDepth = 3, + parameter logic [RegDataWidth-1:0] RstChipBase = 'h0, + parameter logic [RegDataWidth-1:0] RstChipSpace = 'h1_0000, + parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys, MinFreqMHz), + parameter int unsigned PhyStartupCycles = 300 * 200, + parameter int unsigned SyncStages = 2 +) ( + input logic clk_sys_i, + input logic rst_sys_ni, + input logic clk_phy_i, + input logic rst_phy_ni, +`ifdef TARGET_XILINX + input logic clk_ref200_i, +`endif + input logic test_mode_i, + + input axi_req_t axi_req_i, + output axi_rsp_t axi_rsp_o, + + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + + output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, + output logic [NumPhys-1:0] hyper_ck_o, + output logic [NumPhys-1:0] hyper_ck_no, + output logic [NumPhys-1:0] hyper_rwds_o, + input logic [NumPhys-1:0] hyper_rwds_i, + output logic [NumPhys-1:0] hyper_rwds_oe_o, + input logic [NumPhys-1:0][7:0] hyper_dq_i, + output logic [NumPhys-1:0][7:0] hyper_dq_o, + output logic [NumPhys-1:0] hyper_dq_oe_o, + output logic [NumPhys-1:0] hyper_reset_no +); + + typedef struct packed { + logic [(16*NumPhys)-1:0] data; + logic last; + logic [(2*NumPhys)-1:0] strb; + } hyper_tx_t; + + typedef struct packed { + logic [(16*NumPhys)-1:0] data; + logic last; + logic error; + } hyper_rx_t; + + typedef struct packed { + hyperbus_pkg::hyper_tf_t trans; + logic [NumChips-1:0] cs; + } tf_cdc_t; + + logic clk_backend; + logic clk_backend_90; + logic rst_backend_n; + logic [7:0] backend_tx_clk_delay; + hyperbus_pkg::hyper_cfg_t cfg_frontend_apply; + logic cfg_frontend_apply_valid; + logic cfg_frontend_apply_ready; + hyperbus_pkg::hyper_cfg_t cfg_backend_apply; + logic cfg_backend_apply_valid; + logic cfg_backend_apply_ready; + + hyper_rx_t frontend_rx; + logic frontend_rx_valid; + logic frontend_rx_ready; + hyper_tx_t frontend_tx; + logic frontend_tx_valid; + logic frontend_tx_ready; + logic frontend_b_error; + logic frontend_b_valid; + logic frontend_b_ready; + tf_cdc_t frontend_trans; + logic frontend_trans_valid; + logic frontend_trans_ready; + + hyper_rx_t backend_rx; + logic backend_rx_valid; + logic backend_rx_ready; + hyper_tx_t backend_tx; + logic backend_tx_valid; + logic backend_tx_ready; + logic backend_b_error; + logic backend_b_valid; + logic backend_b_ready; + tf_cdc_t backend_trans; + logic backend_trans_valid; + logic backend_trans_ready; + + logic rst_phy_async_n; + assign rst_phy_async_n = rst_sys_ni & rst_phy_ni; + + assign clk_backend = clk_phy_i; + + rstgen i_rstgen_phy ( + .clk_i ( clk_backend ), + .rst_ni ( rst_phy_async_n ), + .test_mode_i ( test_mode_i ), + .rst_no ( rst_backend_n ), + .init_no ( ) + ); + + hyperbus_tx_clk_delay i_tx_clk_delay ( + .rst_ni ( rst_backend_n ), +`ifdef TARGET_XILINX + .clk_ref200_i ( clk_ref200_i ), +`endif + .clk_i ( clk_backend ), + .in_i ( clk_backend ), + .delay_i ( backend_tx_clk_delay ), + .out_o ( clk_backend_90 ) + ); + + hyperbus_frontend #( + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .axi_rule_t ( axi_rule_t ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .tf_cdc_t ( tf_cdc_t ), + .RegAddrWidth ( RegAddrWidth ), + .RegDataWidth ( RegDataWidth ), + .RstChipBase ( RstChipBase ), + .RstChipSpace ( RstChipSpace ), + .RstCfg ( RstCfg ) + ) i_frontend ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .axi_req_i ( axi_req_i ), + .axi_rsp_o ( axi_rsp_o ), + .reg_req_i ( reg_req_i ), + .reg_rsp_o ( reg_rsp_o ), + .cfg_o ( ), + .cfg_apply_o ( cfg_frontend_apply ), + .cfg_apply_valid_o ( cfg_frontend_apply_valid ), + .cfg_apply_ready_i ( cfg_frontend_apply_ready ), + .rx_i ( frontend_rx ), + .rx_valid_i ( frontend_rx_valid ), + .rx_ready_o ( frontend_rx_ready ), + .tx_o ( frontend_tx ), + .tx_valid_o ( frontend_tx_valid ), + .tx_ready_i ( frontend_tx_ready ), + .b_error_i ( frontend_b_error ), + .b_valid_i ( frontend_b_valid ), + .b_ready_o ( frontend_b_ready ), + .trans_o ( frontend_trans ), + .trans_valid_o ( frontend_trans_valid ), + .trans_ready_i ( frontend_trans_ready ) + ); + + hyperbus_async_bridge #( + .RxFifoLogDepth ( RxFifoLogDepth ), + .TxFifoLogDepth ( TxFifoLogDepth ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .tf_cdc_t ( tf_cdc_t ) + ) i_bridge ( + .clk_sys_i ( clk_sys_i ), + .rst_sys_ni ( rst_sys_ni ), + .clk_phy_i ( clk_backend ), + .rst_phy_ni ( rst_backend_n ), + .cfg_apply_i ( cfg_frontend_apply ), + .frontend_cfg_apply_valid_i ( cfg_frontend_apply_valid ), + .frontend_cfg_apply_ready_o ( cfg_frontend_apply_ready ), + .frontend_rx_o ( frontend_rx ), + .frontend_rx_valid_o ( frontend_rx_valid ), + .frontend_rx_ready_i ( frontend_rx_ready ), + .frontend_tx_i ( frontend_tx ), + .frontend_tx_valid_i ( frontend_tx_valid ), + .frontend_tx_ready_o ( frontend_tx_ready ), + .frontend_b_error_o ( frontend_b_error ), + .frontend_b_valid_o ( frontend_b_valid ), + .frontend_b_ready_i ( frontend_b_ready ), + .frontend_trans_i ( frontend_trans ), + .frontend_trans_valid_i ( frontend_trans_valid ), + .frontend_trans_ready_o ( frontend_trans_ready ), + .backend_rx_i ( backend_rx ), + .backend_rx_valid_i ( backend_rx_valid ), + .backend_rx_ready_o ( backend_rx_ready ), + .backend_tx_o ( backend_tx ), + .backend_tx_valid_o ( backend_tx_valid ), + .backend_tx_ready_i ( backend_tx_ready ), + .backend_b_error_i ( backend_b_error ), + .backend_b_valid_i ( backend_b_valid ), + .backend_b_ready_o ( backend_b_ready ), + .backend_trans_o ( backend_trans ), + .backend_trans_valid_o ( backend_trans_valid ), + .backend_trans_ready_i ( backend_trans_ready ), + .cfg_apply_o ( cfg_backend_apply ), + .cfg_apply_valid_o ( cfg_backend_apply_valid ), + .cfg_apply_ready_i ( cfg_backend_apply_ready ) + ); + + hyperbus_backend #( + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .StartupCycles ( PhyStartupCycles ), + .SyncStages ( SyncStages ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .tf_cdc_t ( tf_cdc_t ), + .RstCfg ( RstCfg ) + ) i_backend ( + .clk_i ( clk_backend ), + .clk_90_i ( clk_backend_90 ), + .rst_ni ( rst_backend_n ), + .test_mode_i ( test_mode_i ), + .cfg_apply_i ( cfg_backend_apply ), + .cfg_apply_valid_i ( cfg_backend_apply_valid ), + .cfg_apply_ready_o ( cfg_backend_apply_ready ), + .busy_o ( ), + .tx_clk_delay_o ( backend_tx_clk_delay ), + .rx_o ( backend_rx ), + .rx_valid_o ( backend_rx_valid ), + .rx_ready_i ( backend_rx_ready ), + .tx_i ( backend_tx ), + .tx_valid_i ( backend_tx_valid ), + .tx_ready_o ( backend_tx_ready ), + .b_error_o ( backend_b_error ), + .b_valid_o ( backend_b_valid ), + .b_ready_i ( backend_b_ready ), + .trans_i ( backend_trans ), + .trans_valid_i ( backend_trans_valid ), + .trans_ready_o ( backend_trans_ready ), + .hyper_cs_no ( hyper_cs_no ), + .hyper_ck_o ( hyper_ck_o ), + .hyper_ck_no ( hyper_ck_no ), + .hyper_rwds_o ( hyper_rwds_o ), + .hyper_rwds_i ( hyper_rwds_i ), + .hyper_rwds_oe_o ( hyper_rwds_oe_o ), + .hyper_dq_i ( hyper_dq_i ), + .hyper_dq_o ( hyper_dq_o ), + .hyper_dq_oe_o ( hyper_dq_oe_o ), + .hyper_reset_no ( hyper_reset_no ) + ); + +endmodule diff --git a/src/hyperbus_axi.sv b/src/hyperbus_axi.sv index bac8778..5ccc40b 100644 --- a/src/hyperbus_axi.sv +++ b/src/hyperbus_axi.sv @@ -501,7 +501,8 @@ module hyperbus_axi #( assign trans_active_o = trans_active_q; assign trans_active_set = trans_handshake; - assign trans_active_reset = (rx_valid_i & rx_ready_o & rx_i.last) | (b_valid_i & b_ready_o); + assign trans_active_reset = (ser_out_rsp.r_valid & ser_out_req.r_ready & ser_out_rsp.r.last) | + (b_valid_i & b_ready_o); // Allow W transfers iff currently engaged in write (AW already received) assign trans_wready_set = trans_active_set & rr_out_req_write; diff --git a/src/hyperbus_backend.sv b/src/hyperbus_backend.sv new file mode 100644 index 0000000..899ff61 --- /dev/null +++ b/src/hyperbus_backend.sv @@ -0,0 +1,106 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +module hyperbus_backend #( + parameter int unsigned NumChips = -1, + parameter int unsigned NumPhys = 2, + parameter int unsigned StartupCycles = 60000, + parameter int unsigned SyncStages = 2, + parameter type hyper_rx_t = logic, + parameter type hyper_tx_t = logic, + parameter type tf_cdc_t = logic, + parameter hyperbus_pkg::hyper_cfg_t RstCfg = '0 +) ( + input logic clk_i, + input logic clk_90_i, + input logic rst_ni, + input logic test_mode_i, + + input hyperbus_pkg::hyper_cfg_t cfg_apply_i, + input logic cfg_apply_valid_i, + output logic cfg_apply_ready_o, + + output logic busy_o, + output logic [7:0] tx_clk_delay_o, + + output hyper_rx_t rx_o, + output logic rx_valid_o, + input logic rx_ready_i, + input hyper_tx_t tx_i, + input logic tx_valid_i, + output logic tx_ready_o, + output logic b_error_o, + output logic b_valid_o, + input logic b_ready_i, + input tf_cdc_t trans_i, + input logic trans_valid_i, + output logic trans_ready_o, + + output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, + output logic [NumPhys-1:0] hyper_ck_o, + output logic [NumPhys-1:0] hyper_ck_no, + output logic [NumPhys-1:0] hyper_rwds_o, + input logic [NumPhys-1:0] hyper_rwds_i, + output logic [NumPhys-1:0] hyper_rwds_oe_o, + input logic [NumPhys-1:0][7:0] hyper_dq_i, + output logic [NumPhys-1:0][7:0] hyper_dq_o, + output logic [NumPhys-1:0] hyper_dq_oe_o, + output logic [NumPhys-1:0] hyper_reset_no +); + + hyperbus_pkg::hyper_cfg_t cfg_q, cfg_d; + logic phy_busy; + logic cfg_apply_fire; + + assign cfg_apply_ready_o = ~phy_busy; + assign cfg_apply_fire = cfg_apply_valid_i & cfg_apply_ready_o; + assign cfg_d = cfg_apply_fire ? cfg_apply_i : cfg_q; + assign busy_o = phy_busy | cfg_apply_valid_i; + assign tx_clk_delay_o = cfg_q.t_tx_clk_delay; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) cfg_q <= RstCfg; + else cfg_q <= cfg_d; + end + + hyperbus_phy_if #( + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .StartupCycles ( StartupCycles ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .SyncStages ( SyncStages ) + ) i_phy ( + .clk_phy_i ( clk_i ), + .clk_phy_i_90 ( clk_90_i ), + .rst_phy_ni ( rst_ni ), + .test_mode_i ( test_mode_i ), + .cfg_i ( cfg_q ), + .busy_o ( phy_busy ), + .rx_o ( rx_o ), + .rx_valid_o ( rx_valid_o ), + .rx_ready_i ( rx_ready_i ), + .tx_i ( tx_i ), + .tx_valid_i ( tx_valid_i ), + .tx_ready_o ( tx_ready_o ), + .b_error_o ( b_error_o ), + .b_valid_o ( b_valid_o ), + .b_ready_i ( b_ready_i ), + .trans_i ( trans_i.trans ), + .trans_cs_i ( trans_i.cs ), + .trans_valid_i ( trans_valid_i ), + .trans_ready_o ( trans_ready_o ), + .hyper_cs_no ( hyper_cs_no ), + .hyper_ck_o ( hyper_ck_o ), + .hyper_ck_no ( hyper_ck_no ), + .hyper_rwds_o ( hyper_rwds_o ), + .hyper_rwds_i ( hyper_rwds_i ), + .hyper_rwds_oe_o ( hyper_rwds_oe_o ), + .hyper_dq_i ( hyper_dq_i ), + .hyper_dq_o ( hyper_dq_o ), + .hyper_dq_oe_o ( hyper_dq_oe_o ), + .hyper_reset_no ( hyper_reset_no ) + ); + +endmodule diff --git a/src/hyperbus_frontend.sv b/src/hyperbus_frontend.sv new file mode 100644 index 0000000..7d08b99 --- /dev/null +++ b/src/hyperbus_frontend.sv @@ -0,0 +1,176 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +module hyperbus_frontend #( + parameter int unsigned NumChips = -1, + parameter int unsigned NumPhys = 2, + parameter int unsigned AxiAddrWidth = -1, + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned AxiIdWidth = -1, + parameter int unsigned AxiUserWidth = -1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type axi_rule_t = logic, + parameter type hyper_rx_t = logic, + parameter type hyper_tx_t = logic, + parameter type tf_cdc_t = logic, + parameter int unsigned RegAddrWidth = -1, + parameter int unsigned RegDataWidth = -1, + parameter logic [RegDataWidth-1:0] RstChipBase = 'h0, + parameter logic [RegDataWidth-1:0] RstChipSpace = 'h1_0000, + parameter hyperbus_pkg::hyper_cfg_t RstCfg = '0 +) ( + input logic clk_i, + input logic rst_ni, + + input axi_req_t axi_req_i, + output axi_rsp_t axi_rsp_o, + + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + + output hyperbus_pkg::hyper_cfg_t cfg_o, + output hyperbus_pkg::hyper_cfg_t cfg_apply_o, + output logic cfg_apply_valid_o, + input logic cfg_apply_ready_i, + + input hyper_rx_t rx_i, + input logic rx_valid_i, + output logic rx_ready_o, + output hyper_tx_t tx_o, + output logic tx_valid_o, + input logic tx_ready_i, + input logic b_error_i, + input logic b_valid_i, + output logic b_ready_o, + output tf_cdc_t trans_o, + output logic trans_valid_o, + input logic trans_ready_i +); + + typedef enum logic [1:0] { + CfgIdle, + CfgSend, + CfgWaitAck + } cfg_state_e; + + axi_rule_t [NumChips-1:0] chip_rules; + logic trans_active; + logic axi_trans_valid; + logic axi_trans_ready; + cfg_state_e cfg_state_d, cfg_state_q; + hyperbus_pkg::hyper_cfg_t cfg_applied_d, cfg_applied_q; + hyperbus_pkg::hyper_cfg_t cfg_pending_d, cfg_pending_q; + logic cfg_changed; + logic cfg_apply_busy; + + assign cfg_changed = cfg_o != cfg_applied_q; + assign cfg_apply_busy = (cfg_state_q != CfgIdle) | cfg_changed; + assign cfg_apply_valid_o = cfg_state_q == CfgSend; + assign cfg_apply_o = cfg_pending_q; + + always_comb begin + cfg_state_d = cfg_state_q; + cfg_applied_d = cfg_applied_q; + cfg_pending_d = cfg_pending_q; + + unique case (cfg_state_q) + CfgIdle: begin + if (cfg_changed) begin + cfg_pending_d = cfg_o; + cfg_state_d = CfgSend; + end + end + CfgSend: begin + if (cfg_apply_ready_i) begin + cfg_state_d = CfgWaitAck; + end + end + CfgWaitAck: begin + if (cfg_apply_ready_i) begin + cfg_applied_d = cfg_pending_q; + cfg_state_d = CfgIdle; + end + end + default: cfg_state_d = CfgIdle; + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cfg_state_q <= CfgIdle; + cfg_applied_q <= RstCfg; + cfg_pending_q <= RstCfg; + end else begin + cfg_state_q <= cfg_state_d; + cfg_applied_q <= cfg_applied_d; + cfg_pending_q <= cfg_pending_d; + end + end + + hyperbus_cfg_regs #( + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .RegAddrWidth ( RegAddrWidth ), + .RegDataWidth ( RegDataWidth ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .rule_t ( axi_rule_t ), + .RstChipBase ( RstChipBase ), + .RstChipSpace ( RstChipSpace ), + .RstCfg ( RstCfg ) + ) i_cfg_regs ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .reg_req_i ( reg_req_i ), + .reg_rsp_o ( reg_rsp_o ), + .cfg_o ( cfg_o ), + .chip_rules_o ( chip_rules ), + .trans_active_i ( trans_active ) + ); + + hyperbus_axi #( + .AxiDataWidth ( AxiDataWidth ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .rule_t ( axi_rule_t ) + ) i_axi_slave ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .axi_req_i ( axi_req_i ), + .axi_rsp_o ( axi_rsp_o ), + .rx_i ( rx_i ), + .rx_valid_i ( rx_valid_i ), + .rx_ready_o ( rx_ready_o ), + .tx_o ( tx_o ), + .tx_valid_o ( tx_valid_o ), + .tx_ready_i ( tx_ready_i ), + .b_error_i ( b_error_i ), + .b_valid_i ( b_valid_i ), + .b_ready_o ( b_ready_o ), + .trans_o ( trans_o.trans ), + .trans_cs_o ( trans_o.cs ), + .trans_valid_o ( axi_trans_valid ), + .trans_ready_i ( axi_trans_ready ), + .chip_rules_i ( chip_rules ), + .which_phy_i ( cfg_o.which_phy ), + .phys_in_use_i ( cfg_o.phys_in_use ), + .addr_mask_msb_i ( cfg_o.address_mask_msb ), + .addr_space_i ( cfg_o.address_space ), + .trans_active_o ( trans_active ) + ); + + assign trans_valid_o = axi_trans_valid & ~cfg_apply_busy; + assign axi_trans_ready = trans_ready_i & ~cfg_apply_busy; + +endmodule diff --git a/src/hyperbus_iso_bridge.sv b/src/hyperbus_iso_bridge.sv new file mode 100644 index 0000000..fdb4577 --- /dev/null +++ b/src/hyperbus_iso_bridge.sv @@ -0,0 +1,226 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +module hyperbus_iso_bridge #( + parameter int unsigned RxFifoLogDepth = 8, + parameter int unsigned TxFifoLogDepth = 3, + parameter type hyper_rx_t = logic, + parameter type hyper_tx_t = logic, + parameter type tf_cdc_t = logic +) ( + input logic clk_sys_i, + input logic rst_sys_ni, + input logic clk_phy_i, + input logic rst_phy_ni, + + input hyperbus_pkg::hyper_cfg_t cfg_apply_i, + input logic frontend_cfg_apply_valid_i, + output logic frontend_cfg_apply_ready_o, + + output hyper_rx_t frontend_rx_o, + output logic frontend_rx_valid_o, + input logic frontend_rx_ready_i, + input hyper_tx_t frontend_tx_i, + input logic frontend_tx_valid_i, + output logic frontend_tx_ready_o, + output logic frontend_b_error_o, + output logic frontend_b_valid_o, + input logic frontend_b_ready_i, + input tf_cdc_t frontend_trans_i, + input logic frontend_trans_valid_i, + output logic frontend_trans_ready_o, + + input hyper_rx_t backend_rx_i, + input logic backend_rx_valid_i, + output logic backend_rx_ready_o, + output hyper_tx_t backend_tx_o, + output logic backend_tx_valid_o, + input logic backend_tx_ready_i, + input logic backend_b_error_i, + input logic backend_b_valid_i, + output logic backend_b_ready_o, + output tf_cdc_t backend_trans_o, + output logic backend_trans_valid_o, + input logic backend_trans_ready_i, + + output hyperbus_pkg::hyper_cfg_t cfg_apply_o, + output logic cfg_apply_valid_o, + input logic cfg_apply_ready_i +); + + hyperbus_pkg::hyper_cfg_t cfg_apply_data_q; + logic cfg_apply_src_fire; + tf_cdc_t trans_data_q; + logic trans_src_fire; + logic b_error_q; + logic b_src_fire; + hyper_tx_t tx_fifo_data; + logic tx_fifo_valid; + logic tx_fifo_ready; + logic [TxFifoLogDepth-1:0] tx_fifo_usage; + hyper_tx_t tx_data_q; + logic tx_src_fire; + hyper_rx_t rx_src_fifo_data; + logic rx_src_fifo_valid; + logic rx_src_fifo_ready; + logic [RxFifoLogDepth-1:0] rx_src_fifo_usage; + hyper_rx_t rx_data_q; + logic rx_iso_valid; + logic rx_iso_ready; + logic rx_src_fire; + hyper_rx_t rx_fifo_data; + logic rx_fifo_valid; + logic rx_fifo_ready; + logic [RxFifoLogDepth-1:0] rx_fifo_usage; + + assign cfg_apply_src_fire = frontend_cfg_apply_valid_i & frontend_cfg_apply_ready_o; + assign trans_src_fire = frontend_trans_valid_i & frontend_trans_ready_o; + assign tx_src_fire = tx_fifo_valid & tx_fifo_ready; + assign rx_src_fire = rx_src_fifo_valid & rx_src_fifo_ready; + assign b_src_fire = backend_b_valid_i & backend_b_ready_o; + + always_ff @(posedge clk_sys_i or negedge rst_sys_ni) begin + if (!rst_sys_ni) begin + cfg_apply_data_q <= '0; + trans_data_q <= '0; + tx_data_q <= '0; + end else begin + if (cfg_apply_src_fire) cfg_apply_data_q <= cfg_apply_i; + if (trans_src_fire) trans_data_q <= frontend_trans_i; + if (tx_src_fire) tx_data_q <= tx_fifo_data; + end + end + + always_ff @(posedge clk_phy_i or negedge rst_phy_ni) begin + if (!rst_phy_ni) begin + b_error_q <= 1'b0; + rx_data_q <= '0; + end else begin + if (b_src_fire) b_error_q <= backend_b_error_i; + if (rx_src_fire) rx_data_q <= rx_src_fifo_data; + end + end + + isochronous_4phase_handshake i_iso_cfg_apply ( + .src_clk_i ( clk_sys_i ), + .src_rst_ni ( rst_sys_ni ), + .src_valid_i ( frontend_cfg_apply_valid_i ), + .src_ready_o ( frontend_cfg_apply_ready_o ), + .dst_clk_i ( clk_phy_i ), + .dst_rst_ni ( rst_phy_ni ), + .dst_valid_o ( cfg_apply_valid_o ), + .dst_ready_i ( cfg_apply_ready_i ) + ); + + assign cfg_apply_o = cfg_apply_data_q; + + isochronous_4phase_handshake i_iso_trans ( + .src_clk_i ( clk_sys_i ), + .src_rst_ni ( rst_sys_ni ), + .src_valid_i ( frontend_trans_valid_i ), + .src_ready_o ( frontend_trans_ready_o ), + .dst_clk_i ( clk_phy_i ), + .dst_rst_ni ( rst_phy_ni ), + .dst_valid_o ( backend_trans_valid_o ), + .dst_ready_i ( backend_trans_ready_i ) + ); + + assign backend_trans_o = trans_data_q; + + isochronous_4phase_handshake i_iso_b ( + .src_clk_i ( clk_phy_i ), + .src_rst_ni ( rst_phy_ni ), + .src_valid_i ( backend_b_valid_i ), + .src_ready_o ( backend_b_ready_o ), + .dst_clk_i ( clk_sys_i ), + .dst_rst_ni ( rst_sys_ni ), + .dst_valid_o ( frontend_b_valid_o ), + .dst_ready_i ( frontend_b_ready_i ) + ); + + assign frontend_b_error_o = b_error_q; + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( 1 << TxFifoLogDepth ), + .T ( hyper_tx_t ) + ) i_tx_fifo ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( tx_fifo_usage ), + .data_i ( frontend_tx_i ), + .valid_i ( frontend_tx_valid_i ), + .ready_o ( frontend_tx_ready_o ), + .data_o ( tx_fifo_data ), + .valid_o ( tx_fifo_valid ), + .ready_i ( tx_fifo_ready ) + ); + + isochronous_4phase_handshake i_iso_tx ( + .src_clk_i ( clk_sys_i ), + .src_rst_ni ( rst_sys_ni ), + .src_valid_i ( tx_fifo_valid ), + .src_ready_o ( tx_fifo_ready ), + .dst_clk_i ( clk_phy_i ), + .dst_rst_ni ( rst_phy_ni ), + .dst_valid_o ( backend_tx_valid_o ), + .dst_ready_i ( backend_tx_ready_i ) + ); + + assign backend_tx_o = tx_data_q; + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( 1 << RxFifoLogDepth ), + .T ( hyper_rx_t ) + ) i_rx_src_fifo ( + .clk_i ( clk_phy_i ), + .rst_ni ( rst_phy_ni ), + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( rx_src_fifo_usage ), + .data_i ( backend_rx_i ), + .valid_i ( backend_rx_valid_i ), + .ready_o ( backend_rx_ready_o ), + .data_o ( rx_src_fifo_data ), + .valid_o ( rx_src_fifo_valid ), + .ready_i ( rx_src_fifo_ready ) + ); + + isochronous_4phase_handshake i_iso_rx ( + .src_clk_i ( clk_phy_i ), + .src_rst_ni ( rst_phy_ni ), + .src_valid_i ( rx_src_fifo_valid ), + .src_ready_o ( rx_src_fifo_ready ), + .dst_clk_i ( clk_sys_i ), + .dst_rst_ni ( rst_sys_ni ), + .dst_valid_o ( rx_iso_valid ), + .dst_ready_i ( rx_iso_ready ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( 1 << RxFifoLogDepth ), + .T ( hyper_rx_t ) + ) i_rx_fifo ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( rx_fifo_usage ), + .data_i ( rx_data_q ), + .valid_i ( rx_iso_valid ), + .ready_o ( rx_iso_ready ), + .data_o ( rx_fifo_data ), + .valid_o ( rx_fifo_valid ), + .ready_i ( rx_fifo_ready ) + ); + + assign frontend_rx_o = rx_fifo_data; + assign frontend_rx_valid_o = rx_fifo_valid; + assign rx_fifo_ready = frontend_rx_ready_i; + +endmodule diff --git a/src/hyperbus_isochronous.sv b/src/hyperbus_isochronous.sv new file mode 100644 index 0000000..6abd493 --- /dev/null +++ b/src/hyperbus_isochronous.sv @@ -0,0 +1,261 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +module hyperbus_isochronous #( + parameter int unsigned NumChips = -1, + parameter int unsigned NumPhys = 2, + parameter int unsigned AxiAddrWidth = -1, + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned AxiIdWidth = -1, + parameter int unsigned AxiUserWidth = -1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter type axi_w_chan_t = logic, + parameter type axi_b_chan_t = logic, + parameter type axi_ar_chan_t = logic, + parameter type axi_r_chan_t = logic, + parameter type axi_aw_chan_t = logic, + parameter int unsigned RegAddrWidth = -1, + parameter int unsigned RegDataWidth = -1, + parameter int unsigned MinFreqMHz = 100, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type axi_rule_t = logic, + parameter int unsigned RxFifoLogDepth = 8, + parameter int unsigned TxFifoLogDepth = 3, + parameter logic [RegDataWidth-1:0] RstChipBase = 'h0, + parameter logic [RegDataWidth-1:0] RstChipSpace = 'h1_0000, + parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys, MinFreqMHz), + parameter int unsigned PhyStartupCycles = 300 * 200, + parameter int unsigned SyncStages = 2 +) ( + input logic clk_sys_i, + input logic rst_sys_ni, +`ifdef TARGET_XILINX + input logic clk_ref200_i, +`endif + input logic test_mode_i, + + input axi_req_t axi_req_i, + output axi_rsp_t axi_rsp_o, + + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + + output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, + output logic [NumPhys-1:0] hyper_ck_o, + output logic [NumPhys-1:0] hyper_ck_no, + output logic [NumPhys-1:0] hyper_rwds_o, + input logic [NumPhys-1:0] hyper_rwds_i, + output logic [NumPhys-1:0] hyper_rwds_oe_o, + input logic [NumPhys-1:0][7:0] hyper_dq_i, + output logic [NumPhys-1:0][7:0] hyper_dq_o, + output logic [NumPhys-1:0] hyper_dq_oe_o, + output logic [NumPhys-1:0] hyper_reset_no +); + + typedef struct packed { + logic [(16*NumPhys)-1:0] data; + logic last; + logic [(2*NumPhys)-1:0] strb; + } hyper_tx_t; + + typedef struct packed { + logic [(16*NumPhys)-1:0] data; + logic last; + logic error; + } hyper_rx_t; + + typedef struct packed { + hyperbus_pkg::hyper_tf_t trans; + logic [NumChips-1:0] cs; + } tf_cdc_t; + + logic clk_backend; + logic clk_backend_90; + logic rst_backend_n; + hyperbus_pkg::hyper_cfg_t cfg_frontend_apply; + logic cfg_frontend_apply_valid; + logic cfg_frontend_apply_ready; + hyperbus_pkg::hyper_cfg_t cfg_backend_apply; + logic cfg_backend_apply_valid; + logic cfg_backend_apply_ready; + logic rst_backend_async_n; + + hyper_rx_t frontend_rx; + logic frontend_rx_valid; + logic frontend_rx_ready; + hyper_tx_t frontend_tx; + logic frontend_tx_valid; + logic frontend_tx_ready; + logic frontend_b_error; + logic frontend_b_valid; + logic frontend_b_ready; + tf_cdc_t frontend_trans; + logic frontend_trans_valid; + logic frontend_trans_ready; + + hyper_rx_t backend_rx; + logic backend_rx_valid; + logic backend_rx_ready; + hyper_tx_t backend_tx; + logic backend_tx_valid; + logic backend_tx_ready; + logic backend_b_error; + logic backend_b_valid; + logic backend_b_ready; + tf_cdc_t backend_trans; + logic backend_trans_valid; + logic backend_trans_ready; + + hyperbus_clk_gen i_clk_gen ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .clk0_o ( clk_backend ), + .clk90_o ( clk_backend_90 ), + .clk180_o ( ), + .clk270_o ( ), + .rst_no ( rst_backend_async_n ) + ); + + rstgen i_rstgen_backend ( + .clk_i ( clk_backend ), + .rst_ni ( rst_backend_async_n ), + .test_mode_i ( test_mode_i ), + .rst_no ( rst_backend_n ), + .init_no ( ) + ); + + hyperbus_frontend #( + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .axi_rule_t ( axi_rule_t ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .tf_cdc_t ( tf_cdc_t ), + .RegAddrWidth ( RegAddrWidth ), + .RegDataWidth ( RegDataWidth ), + .RstChipBase ( RstChipBase ), + .RstChipSpace ( RstChipSpace ), + .RstCfg ( RstCfg ) + ) i_frontend ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .axi_req_i ( axi_req_i ), + .axi_rsp_o ( axi_rsp_o ), + .reg_req_i ( reg_req_i ), + .reg_rsp_o ( reg_rsp_o ), + .cfg_o ( ), + .cfg_apply_o ( cfg_frontend_apply ), + .cfg_apply_valid_o ( cfg_frontend_apply_valid ), + .cfg_apply_ready_i ( cfg_frontend_apply_ready ), + .rx_i ( frontend_rx ), + .rx_valid_i ( frontend_rx_valid ), + .rx_ready_o ( frontend_rx_ready ), + .tx_o ( frontend_tx ), + .tx_valid_o ( frontend_tx_valid ), + .tx_ready_i ( frontend_tx_ready ), + .b_error_i ( frontend_b_error ), + .b_valid_i ( frontend_b_valid ), + .b_ready_o ( frontend_b_ready ), + .trans_o ( frontend_trans ), + .trans_valid_o ( frontend_trans_valid ), + .trans_ready_i ( frontend_trans_ready ) + ); + + hyperbus_iso_bridge #( + .RxFifoLogDepth ( RxFifoLogDepth ), + .TxFifoLogDepth ( TxFifoLogDepth ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .tf_cdc_t ( tf_cdc_t ) + ) i_bridge ( + .clk_sys_i ( clk_sys_i ), + .rst_sys_ni ( rst_sys_ni ), + .clk_phy_i ( clk_backend ), + .rst_phy_ni ( rst_backend_n ), + .cfg_apply_i ( cfg_frontend_apply ), + .frontend_cfg_apply_valid_i ( cfg_frontend_apply_valid ), + .frontend_cfg_apply_ready_o ( cfg_frontend_apply_ready ), + .frontend_rx_o ( frontend_rx ), + .frontend_rx_valid_o ( frontend_rx_valid ), + .frontend_rx_ready_i ( frontend_rx_ready ), + .frontend_tx_i ( frontend_tx ), + .frontend_tx_valid_i ( frontend_tx_valid ), + .frontend_tx_ready_o ( frontend_tx_ready ), + .frontend_b_error_o ( frontend_b_error ), + .frontend_b_valid_o ( frontend_b_valid ), + .frontend_b_ready_i ( frontend_b_ready ), + .frontend_trans_i ( frontend_trans ), + .frontend_trans_valid_i ( frontend_trans_valid ), + .frontend_trans_ready_o ( frontend_trans_ready ), + .backend_rx_i ( backend_rx ), + .backend_rx_valid_i ( backend_rx_valid ), + .backend_rx_ready_o ( backend_rx_ready ), + .backend_tx_o ( backend_tx ), + .backend_tx_valid_o ( backend_tx_valid ), + .backend_tx_ready_i ( backend_tx_ready ), + .backend_b_error_i ( backend_b_error ), + .backend_b_valid_i ( backend_b_valid ), + .backend_b_ready_o ( backend_b_ready ), + .backend_trans_o ( backend_trans ), + .backend_trans_valid_o ( backend_trans_valid ), + .backend_trans_ready_i ( backend_trans_ready ), + .cfg_apply_o ( cfg_backend_apply ), + .cfg_apply_valid_o ( cfg_backend_apply_valid ), + .cfg_apply_ready_i ( cfg_backend_apply_ready ) + ); + + hyperbus_backend #( + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .StartupCycles ( PhyStartupCycles ), + .SyncStages ( SyncStages ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .tf_cdc_t ( tf_cdc_t ), + .RstCfg ( RstCfg ) + ) i_backend ( + .clk_i ( clk_backend ), + .clk_90_i ( clk_backend_90 ), + .rst_ni ( rst_backend_n ), + .test_mode_i ( test_mode_i ), + .cfg_apply_i ( cfg_backend_apply ), + .cfg_apply_valid_i ( cfg_backend_apply_valid ), + .cfg_apply_ready_o ( cfg_backend_apply_ready ), + .busy_o ( ), + .tx_clk_delay_o ( ), + .rx_o ( backend_rx ), + .rx_valid_o ( backend_rx_valid ), + .rx_ready_i ( backend_rx_ready ), + .tx_i ( backend_tx ), + .tx_valid_i ( backend_tx_valid ), + .tx_ready_o ( backend_tx_ready ), + .b_error_o ( backend_b_error ), + .b_valid_o ( backend_b_valid ), + .b_ready_i ( backend_b_ready ), + .trans_i ( backend_trans ), + .trans_valid_i ( backend_trans_valid ), + .trans_ready_o ( backend_trans_ready ), + .hyper_cs_no ( hyper_cs_no ), + .hyper_ck_o ( hyper_ck_o ), + .hyper_ck_no ( hyper_ck_no ), + .hyper_rwds_o ( hyper_rwds_o ), + .hyper_rwds_i ( hyper_rwds_i ), + .hyper_rwds_oe_o ( hyper_rwds_oe_o ), + .hyper_dq_i ( hyper_dq_i ), + .hyper_dq_o ( hyper_dq_o ), + .hyper_dq_oe_o ( hyper_dq_oe_o ), + .hyper_reset_no ( hyper_reset_no ) + ); + +endmodule diff --git a/src/hyperbus_phy.sv b/src/hyperbus_phy.sv index 11c9f84..cf22b44 100644 --- a/src/hyperbus_phy.sv +++ b/src/hyperbus_phy.sv @@ -59,6 +59,13 @@ module hyperbus_phy import hyperbus_pkg::*; #( output logic hyper_reset_no ); + localparam int unsigned RxFifoDepth = 2 ** RxFifoLogDepth; + // Stop before the RWDS CDC FIFO can become completely full. The extra + // margin covers pointer synchronization and the delayed effect of CK gating. + localparam int unsigned RxFifoStopMargin = SyncStages + 2; + localparam int unsigned RxOutstandingLimit = (RxFifoDepth > RxFifoStopMargin) ? + (RxFifoDepth - RxFifoStopMargin) : 1; + logic [1:0] phys_in_use; assign phys_in_use = (NumPhys==2) ? (cfg_i.phys_in_use + 1) : 1; @@ -92,6 +99,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( logic ctl_rclk_ena; logic ctl_rcnt_ena; logic ctl_wclk_ena; + logic rx_outstanding_room; // Command-address hyper_phy_ca_t ca; @@ -201,10 +209,12 @@ module hyperbus_phy import hyperbus_pkg::*; #( assign trx_rx_ready = rx_ready_i; assign rx_valid_o = trx_rx_valid & (r_outstand_q != '0); - // Suspend clock one cycle for every stall caused by upstream. - // This ensures that a sufficiently large RX FIFO will not overflow. - assign ctl_rclk_ena = ~(rx_valid_o & ~rx_ready_i); - // Disable incoming RWDS capture once all launched read words have drained. + assign rx_outstanding_room = r_outstand_q < RxOutstandingLimit; + // Suspend CK for visible downstream stalls and before the RWDS CDC FIFO can + // fill with already-launched, not-yet-drained read words. + assign ctl_rclk_ena = rx_outstanding_room & ~(rx_valid_o & ~rx_ready_i); + // Keep RWDS sampling enabled until all read words launched before a CS break + // have crossed back into the PHY clock domain. assign trx_rx_clk_reset = (state_q != Read) & (r_outstand_q == '0); // Counter for outstanding R responses @@ -265,9 +275,10 @@ module hyperbus_phy import hyperbus_pkg::*; #( Idle: begin trx_cs_ena = 1'b0; timer_d = timer_q; - // Signal ready for, pop next transfer if Write response sent - trans_ready_o = 1'b1; - if (trans_valid_i & ~b_pending_q & r_outstand_q == '0) begin + // Accept the next transfer only after pending responses and + // read samples from the previous segment have drained. + trans_ready_o = ~b_pending_q & (r_outstand_q == '0); + if (trans_valid_i & trans_ready_o) begin tf_d = trans_i; cs_d = trans_cs_i; add_latency_d = 1'b0; @@ -421,6 +432,10 @@ module hyperbus_phy import hyperbus_pkg::*; #( if (ctl_timer_rwr_done) begin if (ctl_tf_burst_done) begin state_d = Idle; + end else if (!tf_q.write && (r_outstand_q != '0)) begin + // Before starting the next read segment, let all samples + // from the previous segment drain and reset RWDS sampling. + timer_d = timer_q; end else begin state_d = SendCA; // Re-enable the io driver if we immediately start the diff --git a/src/hyperbus_phy2r.sv b/src/hyperbus_phy2r.sv index 5415e4c..f66eabc 100644 --- a/src/hyperbus_phy2r.sv +++ b/src/hyperbus_phy2r.sv @@ -53,17 +53,21 @@ module hyperbus_phy2r #( logic is_16_bw, is_8_bw; logic [WordCntWidth-1:0] word_cnt; logic enough_data; + logic enough_data_q; logic sent_available_data; logic [BurstLength-1:0] next_axi_addr; + logic axi_last; assign word_cnt = (AxiBytesInPhyBeat==1) ? '0 : byte_phy_cnt_q[($clog2(NumPhys)+1) +:WordCntWidth]; assign next_axi_addr = ((byte_axi_addr_q>>size_d)<< size_d) + (1<= next_axi_addr; + assign enough_data_q = byte_phy_cnt_q >= next_axi_addr; assign sent_available_data = byte_axi_addr_d >= byte_phy_cnt_q; + assign axi_last = data_buffer_q.last && (last_addr_q == next_axi_addr); assign data_o.data = data_buffer_q.data; assign data_o.error = data_buffer_q.error; assign data_o.valid = '0; - assign data_o.last = data_buffer_q.last && (last_addr_q==byte_axi_addr_d); + assign data_o.last = axi_last; always_comb begin : counter byte_axi_addr_d = byte_axi_addr_q; @@ -121,12 +125,12 @@ module hyperbus_phy2r #( end end CntReady: begin - phy_ready_o = 1'b0; - axi_valid_o = 1'b1; - if(sent_available_data) begin + axi_valid_o = enough_data_q; + phy_ready_o = !enough_data_q; + if(axi_valid_o && sent_available_data) begin phy_ready_o = 1'b1; state_d = (data_o.last) ? Idle : ( phy_valid_i ? Sample : WaitData ); - end else if (last_addr_q==byte_axi_addr_d) begin + end else if (axi_valid_o && (last_addr_q==byte_axi_addr_d)) begin state_d = Idle; end end diff --git a/src/hyperbus_phy_if.sv b/src/hyperbus_phy_if.sv index baf51f7..1b45310 100644 --- a/src/hyperbus_phy_if.sv +++ b/src/hyperbus_phy_if.sv @@ -6,7 +6,6 @@ // Philippe Sauter module hyperbus_phy_if import hyperbus_pkg::*; #( - parameter bit UsePhyClkDivider = 1, parameter int unsigned NumChips = 2, parameter int unsigned NumPhys = 2, parameter int unsigned StartupCycles = 60000, /*MHz*/ // Conservative maximum frequency estimate @@ -15,14 +14,12 @@ module hyperbus_phy_if import hyperbus_pkg::*; #( parameter type hyper_rx_t = logic )( input logic clk_phy_i, - input logic clk_phy_i_90, // only used together with divided clock (clk_gen) -`ifdef TARGET_XILINX - input logic clk_ref200_i, // only used with Xilinx delay lines (reference to IDELAY cells) -`endif + input logic clk_phy_i_90, input logic rst_phy_ni, input logic test_mode_i, // Config registers input phy_cfg_t cfg_i, + output logic busy_o, // Transactions input logic trans_valid_i, output logic trans_ready_o, @@ -56,30 +53,8 @@ module hyperbus_phy_if import hyperbus_pkg::*; #( logic clk_phy_0, clk_phy_90; - // Shift clock by 90 degrees - if(UsePhyClkDivider == '0) begin : clock_generator - assign clk_phy_0 = clk_phy_i; - - `ifdef TARGET_XILINX - hyperbus_clk_delay i_delay_tx_clk_90 ( - .rst_i ( ~rst_phy_ni ), - .clk_ref200_i, - .clk_i ( clk_phy_i ), - .in_i ( clk_phy_0 ), - .delay_i ( cfg_i.t_tx_clk_delay ), - .out_o ( clk_phy_90 ) - ); - `else - hyperbus_delay i_delay_tx_clk_90 ( - .in_i ( clk_phy_0 ), - .delay_i ( cfg_i.t_tx_clk_delay ), - .out_o ( clk_phy_90 ) - ); - `endif - end else begin - assign clk_phy_0 = clk_phy_i; - assign clk_phy_90 = clk_phy_i_90; - end + assign clk_phy_0 = clk_phy_i; + assign clk_phy_90 = clk_phy_i_90; phy_rx_t [NumPhys-1:0] phy_fifo_rx; @@ -124,6 +99,7 @@ module hyperbus_phy_if import hyperbus_pkg::*; #( end end + assign busy_o = |phy_busy; assign rx_both_valid = & (fifo_axi_valid | ~phy_active_q); assign rx_valid_o = rx_both_valid; assign fifo_axi_ready = rx_ready_i && rx_both_valid; @@ -231,7 +207,7 @@ module hyperbus_phy_if import hyperbus_pkg::*; #( .cfg_i ( cfg_i ), - .busy_o ( ), + .busy_o ( busy_o ), .rx_data_o ( rx_o.data ), .rx_last_o ( rx_o.last ), diff --git a/src/hyperbus_stub.sv b/src/hyperbus_stub.sv deleted file mode 100644 index 548e54d..0000000 --- a/src/hyperbus_stub.sv +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2023 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 -// -// Thomas Benz -// Paul Scheffler - -module hyperbus #( - parameter int unsigned NumChips = -1, - parameter int unsigned AxiDataWidth = -1, - parameter int unsigned AxiIdWidth = -1, - parameter type axi_req_t = logic, - parameter type axi_rsp_t = logic, - parameter type axi_rule_t = logic -) ( - input logic clk_phy_i, - input logic clk_sys_i, - input logic rst_ni, - input logic test_mode_i, - // AXI bus - input axi_req_t axi_req_i, - output axi_rsp_t axi_rsp_o, - // Reg bus - input reg_intf_pkg::req_a32_d32 reg_req_i, - output reg_intf_pkg::rsp_d32 reg_rsp_o, - // PHY interface - output logic [NumChips-1:0] hyper_cs_no, - output logic hyper_ck_o, - output logic hyper_ck_no, - output logic hyper_rwds_o, - input logic hyper_rwds_i, - output logic hyper_rwds_oe_o, - input logic [7:0] hyper_dq_i, - output logic [7:0] hyper_dq_o, - output logic hyper_dq_oe_o, - output logic hyper_reset_no, - // Debug interface - output logic debug_hyper_rwds_oe_o, - output logic debug_hyper_dq_oe_o, - output logic [3:0] debug_hyper_phy_state_o -); - - assign axi_rsp_o = '0; - assign reg_rsp_o = '0; - assign hyper_cs_no = '0; - assign hyper_ck_o = '0; - assign hyper_ck_no = '0; - assign hyper_rwds_o = '0; - assign hyper_rwds_oe_o = '0; - assign hyper_dq_o = '0; - assign hyper_dq_oe_o = '0; - assign hyper_reset_no = '0; - assign debug_hyper_rwds_oe_o = '0; - assign debug_hyper_dq_oe_o = '0; - assign debug_hyper_phy_state_o = '0; - -endmodule : hyperbus diff --git a/src/hyperbus_synchronous.sv b/src/hyperbus_synchronous.sv new file mode 100644 index 0000000..a814286 --- /dev/null +++ b/src/hyperbus_synchronous.sv @@ -0,0 +1,194 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +module hyperbus_synchronous #( + parameter int unsigned NumChips = -1, + parameter int unsigned NumPhys = 2, + parameter int unsigned AxiAddrWidth = -1, + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned AxiIdWidth = -1, + parameter int unsigned AxiUserWidth = -1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter type axi_w_chan_t = logic, + parameter type axi_b_chan_t = logic, + parameter type axi_ar_chan_t = logic, + parameter type axi_r_chan_t = logic, + parameter type axi_aw_chan_t = logic, + parameter int unsigned RegAddrWidth = -1, + parameter int unsigned RegDataWidth = -1, + parameter int unsigned MinFreqMHz = 100, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type axi_rule_t = logic, + parameter logic [RegDataWidth-1:0] RstChipBase = 'h0, + parameter logic [RegDataWidth-1:0] RstChipSpace = 'h1_0000, + parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys, MinFreqMHz), + parameter int unsigned PhyStartupCycles = 300 * 200, + parameter int unsigned SyncStages = 2 +) ( + input logic clk_sys_i, + input logic rst_sys_ni, +`ifdef TARGET_XILINX + input logic clk_ref200_i, +`endif + input logic test_mode_i, + + input axi_req_t axi_req_i, + output axi_rsp_t axi_rsp_o, + + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + + output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, + output logic [NumPhys-1:0] hyper_ck_o, + output logic [NumPhys-1:0] hyper_ck_no, + output logic [NumPhys-1:0] hyper_rwds_o, + input logic [NumPhys-1:0] hyper_rwds_i, + output logic [NumPhys-1:0] hyper_rwds_oe_o, + input logic [NumPhys-1:0][7:0] hyper_dq_i, + output logic [NumPhys-1:0][7:0] hyper_dq_o, + output logic [NumPhys-1:0] hyper_dq_oe_o, + output logic [NumPhys-1:0] hyper_reset_no +); + + typedef struct packed { + logic [(16*NumPhys)-1:0] data; + logic last; + logic [(2*NumPhys)-1:0] strb; + } hyper_tx_t; + + typedef struct packed { + logic [(16*NumPhys)-1:0] data; + logic last; + logic error; + } hyper_rx_t; + + typedef struct packed { + hyperbus_pkg::hyper_tf_t trans; + logic [NumChips-1:0] cs; + } tf_t; + + logic clk_backend_90; + logic [7:0] backend_tx_clk_delay; + hyperbus_pkg::hyper_cfg_t cfg_apply; + logic cfg_apply_valid; + logic cfg_apply_ready; + + hyper_rx_t rx; + logic rx_valid; + logic rx_ready; + hyper_tx_t tx; + logic tx_valid; + logic tx_ready; + tf_t frontend_trans; + logic frontend_trans_valid; + logic frontend_trans_ready; + logic backend_trans_ready; + logic b_error; + logic b_valid; + logic b_ready; + + hyperbus_tx_clk_delay i_tx_clk_delay ( + .rst_ni ( rst_sys_ni ), +`ifdef TARGET_XILINX + .clk_ref200_i ( clk_ref200_i ), +`endif + .clk_i ( clk_sys_i ), + .in_i ( clk_sys_i ), + .delay_i ( backend_tx_clk_delay ), + .out_o ( clk_backend_90 ) + ); + + hyperbus_frontend #( + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .axi_rule_t ( axi_rule_t ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .tf_cdc_t ( tf_t ), + .RegAddrWidth ( RegAddrWidth ), + .RegDataWidth ( RegDataWidth ), + .RstChipBase ( RstChipBase ), + .RstChipSpace ( RstChipSpace ), + .RstCfg ( RstCfg ) + ) i_frontend ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .axi_req_i ( axi_req_i ), + .axi_rsp_o ( axi_rsp_o ), + .reg_req_i ( reg_req_i ), + .reg_rsp_o ( reg_rsp_o ), + .cfg_o ( ), + .cfg_apply_o ( cfg_apply ), + .cfg_apply_valid_o ( cfg_apply_valid ), + .cfg_apply_ready_i ( cfg_apply_ready ), + .rx_i ( rx ), + .rx_valid_i ( rx_valid ), + .rx_ready_o ( rx_ready ), + .tx_o ( tx ), + .tx_valid_o ( tx_valid ), + .tx_ready_i ( tx_ready ), + .b_error_i ( b_error ), + .b_valid_i ( b_valid ), + .b_ready_o ( b_ready ), + .trans_o ( frontend_trans ), + .trans_valid_o ( frontend_trans_valid ), + .trans_ready_i ( frontend_trans_ready ) + ); + + assign frontend_trans_ready = backend_trans_ready; + + hyperbus_backend #( + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .StartupCycles ( PhyStartupCycles ), + .SyncStages ( SyncStages ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .tf_cdc_t ( tf_t ), + .RstCfg ( RstCfg ) + ) i_backend ( + .clk_i ( clk_sys_i ), + .clk_90_i ( clk_backend_90 ), + .rst_ni ( rst_sys_ni ), + .test_mode_i ( test_mode_i ), + .cfg_apply_i ( cfg_apply ), + .cfg_apply_valid_i ( cfg_apply_valid ), + .cfg_apply_ready_o ( cfg_apply_ready ), + .busy_o ( ), + .tx_clk_delay_o ( backend_tx_clk_delay ), + .rx_o ( rx ), + .rx_valid_o ( rx_valid ), + .rx_ready_i ( rx_ready ), + .tx_i ( tx ), + .tx_valid_i ( tx_valid ), + .tx_ready_o ( tx_ready ), + .b_error_o ( b_error ), + .b_valid_o ( b_valid ), + .b_ready_i ( b_ready ), + .trans_i ( frontend_trans ), + .trans_valid_i ( frontend_trans_valid ), + .trans_ready_o ( backend_trans_ready ), + .hyper_cs_no ( hyper_cs_no ), + .hyper_ck_o ( hyper_ck_o ), + .hyper_ck_no ( hyper_ck_no ), + .hyper_rwds_o ( hyper_rwds_o ), + .hyper_rwds_i ( hyper_rwds_i ), + .hyper_rwds_oe_o ( hyper_rwds_oe_o ), + .hyper_dq_i ( hyper_dq_i ), + .hyper_dq_o ( hyper_dq_o ), + .hyper_dq_oe_o ( hyper_dq_oe_o ), + .hyper_reset_no ( hyper_reset_no ) + ); + +endmodule diff --git a/src/hyperbus_synth_wrap.sv b/src/hyperbus_synth_wrap.sv deleted file mode 100644 index e7fc312..0000000 --- a/src/hyperbus_synth_wrap.sv +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2023 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 -// -// Thomas Benz -// Paul Scheffler - -`include "axi/assign.svh" -`include "axi/typedef.svh" -`include "register_interface/typedef.svh" - -module hyperbus_lint_wrap #( - // HyperBus parameters - parameter int unsigned NumChips = 2, - parameter int unsigned NumPhys = 2, - parameter int unsigned IsClockODelayed = 0, - // AXI parameters - parameter int unsigned AxiIdWidth = 6, - parameter int unsigned AxiAddrWidth = 48, - parameter int unsigned AxiDataWidth = 128, - parameter int unsigned AxiUserWidth = 1, - // Regbus parameters - parameter int unsigned RegAddrWidth = 32, - parameter int unsigned RegDataWidth = 32, - // Dependent parameters; do not override! - parameter type axi_addr_t = logic [AxiAddrWidth-1:0], - parameter type axi_data_t = logic [AxiDataWidth-1:0], - parameter type axi_strb_t = logic [AxiDataWidth/8-1:0], - parameter type axi_id_t = logic [AxiIdWidth-1:0], - parameter type axi_user_t = logic [AxiUserWidth-1:0], - parameter type reg_addr_t = logic [RegAddrWidth-1:0], - parameter type reg_data_t = logic [RegDataWidth-1:0], - parameter type reg_strb_t = logic [RegDataWidth/8-1:0] -) ( - // SoC - input logic clk_phy_i, - input logic rst_phy_ni, - input logic clk_sys_i, - input logic rst_sys_ni, - input logic test_mode_i, - - // AXI bus - input axi_id_t axi_aw_id_i, - input axi_addr_t axi_aw_addr_i, - input axi_pkg::len_t axi_aw_len_i, - input axi_pkg::size_t axi_aw_size_i, - input axi_pkg::burst_t axi_aw_burst_i, - input logic axi_aw_lock_i, - input axi_pkg::cache_t axi_aw_cache_i, - input axi_pkg::prot_t axi_aw_prot_i, - input axi_pkg::qos_t axi_aw_qos_i, - input axi_pkg::region_t axi_aw_region_i, - input axi_pkg::atop_t axi_aw_atop_i, - input axi_user_t axi_aw_user_i, - input logic axi_aw_valid_i, - output logic axi_aw_ready_o, - input axi_data_t axi_w_data_i, - input axi_strb_t axi_w_strb_i, - input logic axi_w_last_i, - input axi_user_t axi_w_user_i, - input logic axi_w_valid_i, - output logic axi_w_ready_o, - output axi_id_t axi_b_id_o, - output axi_pkg::resp_t axi_b_resp_o, - output axi_user_t axi_b_user_o, - output logic axi_b_valid_o, - input logic axi_b_ready_i, - input axi_id_t axi_ar_id_i, - input axi_addr_t axi_ar_addr_i, - input axi_pkg::len_t axi_ar_len_i, - input axi_pkg::size_t axi_ar_size_i, - input axi_pkg::burst_t axi_ar_burst_i, - input logic axi_ar_lock_i, - input axi_pkg::cache_t axi_ar_cache_i, - input axi_pkg::prot_t axi_ar_prot_i, - input axi_pkg::qos_t axi_ar_qos_i, - input axi_pkg::region_t axi_ar_region_i, - input axi_user_t axi_ar_user_i, - input logic axi_ar_valid_i, - output logic axi_ar_ready_o, - output axi_id_t axi_r_id_o, - output axi_data_t axi_r_data_o, - output axi_pkg::resp_t axi_r_resp_o, - output logic axi_r_last_o, - output axi_user_t axi_r_user_o, - output logic axi_r_valid_o, - input logic axi_r_ready_i, - - // Reg bus - input reg_addr_t rbus_req_addr_i, - input logic rbus_req_write_i, - input reg_data_t rbus_req_wdata_i, - input reg_strb_t rbus_req_wstrb_i, - input logic rbus_req_valid_i, - output reg_data_t rbus_rsp_rdata_o, - output logic rbus_rsp_ready_o, - output logic rbus_rsp_error_o, - - // PHY interface - output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, - output logic [NumPhys-1:0] hyper_ck_o, - output logic [NumPhys-1:0] hyper_ck_no, - output logic [NumPhys-1:0] hyper_rwds_o, - input logic [NumPhys-1:0] hyper_rwds_i, - output logic [NumPhys-1:0] hyper_rwds_oe_o, - input logic [NumPhys-1:0][7:0] hyper_dq_i, - output logic [NumPhys-1:0][7:0] hyper_dq_o, - output logic [NumPhys-1:0] hyper_dq_oe_o, - output logic [NumPhys-1:0] hyper_reset_no -); - - // Types - `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, axi_addr_t, axi_id_t, axi_user_t) - `AXI_TYPEDEF_W_CHAN_T(w_chan_t, axi_data_t, axi_strb_t, axi_user_t) - `AXI_TYPEDEF_B_CHAN_T(b_chan_t, axi_id_t, axi_user_t) - `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, axi_addr_t, axi_id_t, axi_user_t) - `AXI_TYPEDEF_R_CHAN_T(r_chan_t, axi_data_t, axi_id_t, axi_user_t) - `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_TYPEDEF_RESP_T(axi_rsp_t, b_chan_t, r_chan_t) - - axi_req_t axi_req; - axi_rsp_t axi_rsp; - - `REG_BUS_TYPEDEF_REQ(reg_req_t, reg_addr_t, reg_data_t, reg_strb_t) - `REG_BUS_TYPEDEF_RSP(reg_rsp_t, reg_data_t) - - reg_req_t reg_req; - reg_rsp_t reg_rsp; - - typedef axi_pkg::xbar_rule_32_t axi_rule_t; - - // Wrapped instance - hyperbus #( - .NumChips ( NumChips ), - .NumPhys ( NumPhys ), - .IsClockODelayed ( IsClockODelayed ), - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidth ( AxiIdWidth ), - .AxiUserWidth ( AxiUserWidth ), - .axi_req_t ( axi_req_t ), - .axi_rsp_t ( axi_rsp_t ), - .axi_w_chan_t ( w_chan_t ), - .axi_b_chan_t ( b_chan_t ), - .axi_ar_chan_t ( ar_chan_t ), - .axi_r_chan_t ( r_chan_t ), - .axi_aw_chan_t ( aw_chan_t ), - .RegDataWidth ( RegDataWidth ), - .reg_req_t ( reg_req_t ), - .reg_rsp_t ( reg_rsp_t ), - .axi_rule_t ( axi_rule_t ) - ) i_hyperbus ( - .clk_phy_i ( clk_phy_i ), - .rst_phy_ni ( rst_phy_ni ), - .clk_sys_i ( clk_sys_i ), - .rst_sys_ni ( rst_sys_ni ), - .test_mode_i ( test_mode_i ), - .axi_req_i ( axi_req ), - .axi_rsp_o ( axi_rsp ), - .reg_req_i ( reg_req ), - .reg_rsp_o ( reg_rsp ), - .hyper_cs_no ( hyper_cs_no ), - .hyper_ck_o ( hyper_ck_o ), - .hyper_ck_no ( hyper_ck_no ), - .hyper_rwds_o ( hyper_rwds_o ), - .hyper_rwds_i ( hyper_rwds_i ), - .hyper_rwds_oe_o ( hyper_rwds_oe_o ), - .hyper_dq_i ( hyper_dq_i ), - .hyper_dq_o ( hyper_dq_o ), - .hyper_dq_oe_o ( hyper_dq_oe_o ), - .hyper_reset_no ( hyper_reset_no ) - ); - - // AXI Slave - assign axi_req.aw.id = axi_aw_id_i; - assign axi_req.aw.addr = axi_aw_addr_i; - assign axi_req.aw.len = axi_aw_len_i; - assign axi_req.aw.size = axi_aw_size_i; - assign axi_req.aw.burst = axi_aw_burst_i; - assign axi_req.aw.lock = axi_aw_lock_i; - assign axi_req.aw.cache = axi_aw_cache_i; - assign axi_req.aw.prot = axi_aw_prot_i; - assign axi_req.aw.qos = axi_aw_qos_i; - assign axi_req.aw.region = axi_aw_region_i; - assign axi_req.aw.atop = axi_aw_atop_i; - assign axi_req.aw.user = axi_aw_user_i; - assign axi_req.aw_valid = axi_aw_valid_i; - assign axi_aw_ready_o = axi_rsp.aw_ready; - assign axi_req.w.data = axi_w_data_i; - assign axi_req.w.strb = axi_w_strb_i; - assign axi_req.w.last = axi_w_last_i; - assign axi_req.w.user = axi_w_user_i; - assign axi_req.w_valid = axi_w_valid_i; - assign axi_w_ready_o = axi_rsp.w_ready; - assign axi_b_id_o = axi_rsp.b.id; - assign axi_b_resp_o = axi_rsp.b.resp; - assign axi_b_user_o = axi_rsp.b.user; - assign axi_b_valid_o = axi_rsp.b_valid; - assign axi_req.b_ready = axi_b_ready_i; - assign axi_req.ar.id = axi_ar_id_i; - assign axi_req.ar.addr = axi_ar_addr_i; - assign axi_req.ar.len = axi_ar_len_i; - assign axi_req.ar.size = axi_ar_size_i; - assign axi_req.ar.burst = axi_ar_burst_i; - assign axi_req.ar.lock = axi_ar_lock_i; - assign axi_req.ar.cache = axi_ar_cache_i; - assign axi_req.ar.prot = axi_ar_prot_i; - assign axi_req.ar.qos = axi_ar_qos_i; - assign axi_req.ar.region = axi_ar_region_i; - assign axi_req.ar.user = axi_ar_user_i; - assign axi_req.ar_valid = axi_ar_valid_i; - assign axi_ar_ready_o = axi_rsp.ar_ready; - assign axi_r_id_o = axi_rsp.r.id; - assign axi_r_data_o = axi_rsp.r.data; - assign axi_r_resp_o = axi_rsp.r.resp; - assign axi_r_last_o = axi_rsp.r.last; - assign axi_r_user_o = axi_rsp.r.user; - assign axi_r_valid_o = axi_rsp.r_valid; - assign axi_req.r_ready = axi_r_ready_i; - - // Regbus slave - assign reg_req.addr = rbus_req_addr_i; - assign reg_req.write = rbus_req_write_i; - assign reg_req.wdata = rbus_req_wdata_i; - assign reg_req.wstrb = rbus_req_wstrb_i; - assign reg_req.valid = rbus_req_valid_i; - assign rbus_rsp_rdata_o = reg_rsp.rdata; - assign rbus_rsp_ready_o = reg_rsp.ready; - assign rbus_rsp_error_o = reg_rsp.error; - -endmodule : hyperbus_lint_wrap diff --git a/src/hyperbus_tx_clk_delay.sv b/src/hyperbus_tx_clk_delay.sv new file mode 100644 index 0000000..05c4393 --- /dev/null +++ b/src/hyperbus_tx_clk_delay.sv @@ -0,0 +1,33 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +module hyperbus_tx_clk_delay ( + input logic rst_ni, +`ifdef TARGET_XILINX + input logic clk_ref200_i, +`endif + input logic clk_i, + input logic in_i, + input logic [7:0] delay_i, + output logic out_o +); + +`ifdef TARGET_XILINX + hyperbus_clk_delay i_delay_tx_clk_90 ( + .rst_i ( ~rst_ni ), + .clk_ref200_i ( clk_ref200_i ), + .clk_i ( clk_i ), + .in_i ( in_i ), + .delay_i ( delay_i ), + .out_o ( out_o ) + ); +`else + hyperbus_delay i_delay_tx_clk_90 ( + .in_i ( in_i ), + .delay_i ( delay_i ), + .out_o ( out_o ) + ); +`endif + +endmodule diff --git a/test/axi_hyper_tb.sv b/test/axi_hyper_tb.sv index fcd42cd..fc5a1e5 100644 --- a/test/axi_hyper_tb.sv +++ b/test/axi_hyper_tb.sv @@ -23,10 +23,26 @@ module axi_hyper_tb parameter int unsigned TbNumReads = 32'd1000, /// Cycle time for the TB clock generator parameter time TbCyclTime = 5ns, + /// Cycle time for the optional asynchronous PHY clock + parameter time TbPhyCyclTime = 6ns, /// Application time to the DUT parameter time TbApplTime = 1ns, /// Test time of the DUT - parameter time TbTestTime = 4ns + parameter time TbTestTime = 4ns, + /// DUT variant: 0 is isochronous, 1 is synchronous, 2 is asynchronous + parameter int unsigned TbDutVariant = 0, + /// RX delay-line tap value used by variants with explicit delay lines. + parameter int unsigned TbRxDelayLineTaps = 16, + /// TX delay-line tap value used by variants with explicit delay lines. + parameter int unsigned TbTxDelayLineTaps = 19, + /// Number of AXI beats in the directed slow read/write stress transactions. + parameter int unsigned TbSlowNumBeats = 64, + /// Idle cycles inserted between each accepted AXI beat in the slow stress transactions. + parameter int unsigned TbSlowGapCycles = 64, + /// Temporary t_burst_max used to force HyperBus segment restarts in the slow stress phase. + parameter int unsigned TbSlowBurstMax = 16, + /// Annotate the HyperRAM timing SDF. Disable for fast RTL regressions. + parameter bit TbAnnotateSdf = 1'b1 ); import hyperbus_tb_pkg::*; ///////////////////////////// @@ -60,6 +76,7 @@ module axi_hyper_tb localparam int unsigned TbDramLenWidth = 32'h80000; logic end_of_sim; + logic [31:0] segment_start_count; /////////////////////// @@ -259,6 +276,17 @@ module axi_hyper_tb return data << (8 * addr[$clog2(TbAxiDataWidthFull/8)-1:0]); endfunction + function automatic logic [TbAxiDataWidthFull-1:0] slow_stress_data( + input axi_addr_t addr, + input int unsigned beat + ); + automatic logic [TbAxiDataWidthFull-1:0] data = '0; + for (int unsigned byte_idx = 0; byte_idx < TbAxiDataWidthFull/8; byte_idx++) begin + data[8*byte_idx +: 8] = 8'(addr[7:0] + beat + (byte_idx * 17)); + end + return data; + endfunction + task automatic axi_write_subword( input axi_ctrl_master_t axi_drv, input axi_addr_t addr, @@ -352,6 +380,122 @@ module axi_hyper_tb end endtask + task automatic axi_write_slow( + input axi_ctrl_master_t axi_drv, + input axi_addr_t addr, + input int unsigned num_beats, + input int unsigned gap_cycles + ); + axi_ctrl_master_t::ax_beat_t ax = new(); + axi_ctrl_master_t::w_beat_t w; + axi_ctrl_master_t::b_beat_t b; + + if (num_beats == 0 || num_beats > 256) begin + $fatal(1, "Slow AXI write num_beats must be in [1, 256], got %0d", num_beats); + end + + ax.ax_addr = addr; + ax.ax_id = '0; + ax.ax_len = 8'(num_beats - 1); + ax.ax_size = $clog2(TbAxiDataWidthFull/8); + ax.ax_burst = axi_pkg::BURST_INCR; + axi_drv.send_aw(ax); + + for (int unsigned beat = 0; beat < num_beats; beat++) begin + repeat (gap_cycles) @(posedge clk); + w = new(); + w.w_data = slow_stress_data(addr, beat); + w.w_strb = '1; + w.w_last = beat == (num_beats - 1); + axi_drv.send_w(w); + end + + axi_drv.recv_b(b); + if (b.b_resp != axi_pkg::RESP_OKAY) begin + $error("[AXI-SLOW] Write to 0x%08x returned response %0d", addr, b.b_resp); + end + endtask + + task automatic axi_read_slow_check( + input axi_ctrl_master_t axi_drv, + input axi_addr_t addr, + input int unsigned num_beats, + input int unsigned gap_cycles + ); + axi_ctrl_master_t::ax_beat_t ax = new(); + axi_ctrl_master_t::r_beat_t r; + logic [TbAxiDataWidthFull-1:0] expected; + + if (num_beats == 0 || num_beats > 256) begin + $fatal(1, "Slow AXI read num_beats must be in [1, 256], got %0d", num_beats); + end + + ax.ax_addr = addr; + ax.ax_id = '0; + ax.ax_len = 8'(num_beats - 1); + ax.ax_size = $clog2(TbAxiDataWidthFull/8); + ax.ax_burst = axi_pkg::BURST_INCR; + axi_drv.send_ar(ax); + + for (int unsigned beat = 0; beat < num_beats; beat++) begin + repeat (gap_cycles) @(posedge clk); + axi_drv.recv_r(r); + expected = slow_stress_data(addr, beat); + if (r.r_resp != axi_pkg::RESP_OKAY || r.r_data != expected || + r.r_last != (beat == (num_beats - 1))) begin + $error("[AXI-SLOW] Read beat %0d from 0x%08x returned data=0x%016x last=%0b resp=%0d, expected data=0x%016x last=%0b", + beat, addr, r.r_data, r.r_last, r.r_resp, expected, beat == (num_beats - 1)); + end + end + endtask + + task automatic run_slow_backpressure_test( + input axi_ctrl_master_t axi_drv, + input reg_bus_master_t reg_drv + ); + localparam axi_addr_t SlowBaseAddr = axi_addr_t'(32'h8000_4000); + logic [RegBusDW-1:0] saved_t_burst_max; + logic [31:0] write_segment_starts; + logic [31:0] read_segment_starts; + logic [31:0] segment_start_snapshot; + logic reg_error; + + $display("==========================="); + $display("= Slow AXI backpressure ="); + $display("==========================="); + + reg_drv.send_read(32'h2 << 2, saved_t_burst_max, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + + reg_drv.send_write(32'h2 << 2, TbSlowBurstMax, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + + segment_start_snapshot = segment_start_count; + axi_write_slow(axi_drv, SlowBaseAddr, TbSlowNumBeats, TbSlowGapCycles); + write_segment_starts = segment_start_count - segment_start_snapshot; + if (write_segment_starts <= 1) begin + $error("[AXI-SLOW] Write observed %0d HyperBus segment start(s), expected at least one restart", + write_segment_starts); + end else begin + $display("[AXI-SLOW] Write observed %0d HyperBus segment starts (%0d restarts)", + write_segment_starts, write_segment_starts - 1); + end + + segment_start_snapshot = segment_start_count; + axi_read_slow_check(axi_drv, SlowBaseAddr, TbSlowNumBeats, TbSlowGapCycles); + read_segment_starts = segment_start_count - segment_start_snapshot; + if (read_segment_starts <= 1) begin + $error("[AXI-SLOW] Read observed %0d HyperBus segment start(s), expected at least one restart", + read_segment_starts); + end else begin + $display("[AXI-SLOW] Read observed %0d HyperBus segment starts (%0d restarts)", + read_segment_starts, read_segment_starts - 1); + end + + reg_drv.send_write(32'h2 << 2, saved_t_burst_max, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + endtask + initial begin : proc_sim_crtl automatic axi_scoreboard_mst_t mst_scoreboard = new( score_mst_intf_dv ); @@ -375,19 +519,30 @@ module axi_hyper_tb @(posedge rst_n); mst_scoreboard.monitor(); + if (TbDutVariant != 0) begin + reg_master.send_write(32'h4 << 2, TbRxDelayLineTaps, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_write(32'h5 << 2, TbTxDelayLineTaps, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + end + #600350ns; - // switch memory address space to register space - reg_master.send_write(32'h7<<2, 1'b1, '1, s_reg_error); - if (s_reg_error != 1'b0) $error("unexpected error"); + run_slow_backpressure_test(axi_ctrl_mst, reg_master); - // enable variable latency so we can test RWDS sampling - s27ks_cfg0.fixed_latency_enable = 1'b0; - axi_write_32(32'h8000_0000 + S27KS_CFG0_REG_OFFSET, (s27ks_cfg0 | s27ks_cfg0 << 16)); + if (TbDutVariant == 0) begin + // switch memory address space to register space + reg_master.send_write(32'h7<<2, 1'b1, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); - // switch back to memory address space - reg_master.send_write(32'h7<<2, 1'b0, '1, s_reg_error); - if (s_reg_error != 1'b0) $error("unexpected error"); + // enable variable latency so we can test RWDS sampling + s27ks_cfg0.fixed_latency_enable = 1'b0; + axi_write_32(32'h8000_0000 + S27KS_CFG0_REG_OFFSET, (s27ks_cfg0 | s27ks_cfg0 << 16)); + + // switch back to memory address space + reg_master.send_write(32'h7<<2, 1'b0, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + end check_consecutive_reads(axi_ctrl_mst); @@ -473,15 +628,47 @@ module axi_hyper_tb .NumChips ( NumChips ), .NumPhys ( NumPhys ), + .AnnotateSdf ( TbAnnotateSdf ), .IsClockODelayed ( IsClockODelayed ), + .DutVariant ( TbDutVariant ), + .PhyCyclTime ( TbPhyCyclTime ), .axi_rule_t ( rule_t ) ) i_dut_if ( // clk and rst signal .clk_i ( clk ), .rst_ni ( rst_n ), .end_sim_i ( end_of_sim ), + .segment_start_count_o ( segment_start_count ), .axi_slv_if ( axi_dut_intf ), .reg_slv_if ( reg_bus_mst ) ); endmodule + +module axi_hyper_tb_isochronous; + axi_hyper_tb #( + .TbDutVariant ( 0 ), + .TbCyclTime ( 5ns ), + .TbPhyCyclTime ( 6ns ) + ) i_axi_hyper_tb (); +endmodule + +module axi_hyper_tb_synchronous; + axi_hyper_tb #( + .TbDutVariant ( 1 ), + .TbCyclTime ( 10ns ), + .TbPhyCyclTime ( 10ns ), + .TbRxDelayLineTaps ( 16 ), + .TbTxDelayLineTaps ( 31 ) + ) i_axi_hyper_tb (); +endmodule + +module axi_hyper_tb_asynchronous; + axi_hyper_tb #( + .TbDutVariant ( 2 ), + .TbCyclTime ( 5ns ), + .TbPhyCyclTime ( 6ns ), + .TbRxDelayLineTaps ( 16 ), + .TbTxDelayLineTaps ( 19 ) + ) i_axi_hyper_tb (); +endmodule diff --git a/test/dut_if.sv b/test/dut_if.sv index e63d9e9..65b016f 100644 --- a/test/dut_if.sv +++ b/test/dut_if.sv @@ -24,12 +24,16 @@ module dut_if parameter int NumChips = -1, parameter int NumPhys = -1, + parameter bit AnnotateSdf = 1'b1, parameter int IsClockODelayed = -1, + parameter int unsigned DutVariant = 0, + parameter time PhyCyclTime = 6ns, parameter type axi_rule_t = logic )( input logic clk_i, input logic rst_ni, input logic end_sim_i, + output logic [31:0] segment_start_count_o, AXI_BUS.Slave axi_slv_if, REG_BUS.in reg_slv_if @@ -80,6 +84,9 @@ module dut_if logic [NumPhys-1:0][7:0] hyper_dq_o; logic [NumPhys-1:0] hyper_dq_oe; logic [NumPhys-1:0] hyper_reset_n_wire; + logic phy_clk; + logic [NumPhys-1:0][NumChips-1:0] hyper_cs_n_q; + logic segment_start; wire [NumPhys-1:0][NumChips-1:0] pad_hyper_csn; wire [NumPhys-1:0] pad_hyper_ck; @@ -122,8 +129,31 @@ module dut_if .r_ready_i ( axi_req.r_ready ) ); + initial begin + phy_clk = 1'b0; + forever begin + #(PhyCyclTime/2); + phy_clk = ~phy_clk; + end + end + + assign segment_start = |(hyper_cs_n_q & ~hyper_cs_n_wire); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + hyper_cs_n_q <= '1; + segment_start_count_o <= '0; + end else begin + hyper_cs_n_q <= hyper_cs_n_wire; + if (segment_start) begin + segment_start_count_o <= segment_start_count_o + 1; + end + end + end + // DUT - hyperbus #( + hyperbus_test_dut #( + .DutVariant ( DutVariant ), .NumChips ( NumChips ), .NumPhys ( NumPhys ), .AxiAddrWidth ( AxiAddrWidth ), @@ -140,13 +170,15 @@ module dut_if .RegDataWidth ( RegDw ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), - .UsePhyClkDivider ( 1 ), .axi_rule_t ( axi_rule_t ) ) i_dut ( - .clk_phy_i ( clk_i ), - .rst_phy_ni ( rst_ni ), .clk_sys_i ( clk_i ), .rst_sys_ni ( rst_ni ), + .clk_phy_i ( phy_clk ), + .rst_phy_ni ( rst_ni ), +`ifdef TARGET_XILINX + .clk_ref200_i ( clk_i ), +`endif .test_mode_i ( 1'b0 ), .axi_req_i ( axi_req ), .axi_rsp_o ( axi_resp ), @@ -193,18 +225,18 @@ module dut_if end // block: hyperrams endgenerate - generate + if (AnnotateSdf) begin : gen_sdf_annotation for (genvar p=0; p Date: Thu, 23 Jul 2026 17:18:59 +0200 Subject: [PATCH 07/11] Refactor HyperBus host and configuration flow Separate protocol-facing configuration, midend request processing, clock-domain bridges, and backend control. Add explicit drain/apply sequencing and harden segmented transaction handling. --- Bender.yml | 6 +- docs/regs/hyperbus_cfg_regs.md | 53 +- include/hyperbus/typedef.svh | 47 ++ include/hyperbus_cfg_regs.h | 34 +- scripts/start.tcl | 12 +- src/hyperbus_async_bridge.sv | 217 ++++--- src/hyperbus_asynchronous.sv | 306 ++++++---- src/hyperbus_axi.sv | 729 +++++++++------------- src/hyperbus_backend.sv | 45 +- src/hyperbus_cfg_frontend.sv | 162 +++++ src/hyperbus_cfg_regs.sv | 44 +- src/hyperbus_frontend.sv | 176 ------ src/hyperbus_iso_bridge.sv | 152 +++-- src/hyperbus_isochronous.sv | 290 +++++---- src/hyperbus_midend.sv | 850 ++++++++++++++++++++++++++ src/hyperbus_phy.sv | 19 +- src/hyperbus_phy2r.sv | 274 +++++---- src/hyperbus_phy_if.sv | 202 +++--- src/hyperbus_pkg.sv | 39 +- src/hyperbus_synchronous.sv | 303 ++++++--- src/hyperbus_trx.sv | 31 +- src/hyperbus_w2phy.sv | 371 ++++++----- src/regs/hyperbus_cfg_regblock.sv | 144 +++-- src/regs/hyperbus_cfg_regblock_pkg.sv | 47 +- src/regs/hyperbus_cfg_regs.rdl | 36 +- test/axi_hyper_tb.sv | 573 ++++++++++++++++- test/dut_if.sv | 7 +- test/fixture_hyperbus.sv | 5 - test/hyperbus_cfg_regs_tb.sv | 110 ++-- test/hyperbus_test_dut.sv | 28 - 30 files changed, 3493 insertions(+), 1819 deletions(-) create mode 100644 include/hyperbus/typedef.svh create mode 100644 src/hyperbus_cfg_frontend.sv delete mode 100644 src/hyperbus_frontend.sv create mode 100644 src/hyperbus_midend.sv diff --git a/Bender.yml b/Bender.yml index cb14edc..ada1701 100644 --- a/Bender.yml +++ b/Bender.yml @@ -11,6 +11,9 @@ package: - "Paul Scheffler " - "Luca Valente " # current maintainer +export_include_dirs: + - include + dependencies: common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.21.0 } axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.39.1 } @@ -55,7 +58,8 @@ sources: - src/hyperbus_phy.sv - src/hyperbus_phy_if.sv - src/hyperbus_axi.sv - - src/hyperbus_frontend.sv + - src/hyperbus_midend.sv + - src/hyperbus_cfg_frontend.sv - src/hyperbus_async_bridge.sv - src/hyperbus_iso_bridge.sv - src/hyperbus_backend.sv diff --git a/docs/regs/hyperbus_cfg_regs.md b/docs/regs/hyperbus_cfg_regs.md index 326f98d..7d77eaa 100644 --- a/docs/regs/hyperbus_cfg_regs.md +++ b/docs/regs/hyperbus_cfg_regs.md @@ -23,8 +23,7 @@ Don't override. Generated from: hyperbus_cfg_regs | 0x14 | t_tx_clk_delay | — | | 0x18 | address_mask_msb | — | | 0x1C | address_space | — | -| 0x20 | phys_in_use | — | -| 0x24 | which_phy | — | +| 0x20 | dual_phy | — | | 0x28 | t_csh_cycles | — | | 0x2C | csn_to_ck_cycles | — | | 0x30 | chip0_base | — | @@ -35,6 +34,8 @@ Don't override. Generated from: hyperbus_cfg_regs | 0x44 | chip2_bound | — | | 0x48 | chip3_base | — | | 0x4C | chip3_bound | — | +| 0x50 | flush | — | +| 0x54 | status | — | | 0x58 | chip4_base | — | | 0x5C | chip4_bound | — | | 0x60 | chip5_base | — | @@ -158,25 +159,13 @@ Don't override. Generated from: hyperbus_cfg_regs |----|----------|------|-----|----| | 0 | value | rw | 0x0 | — | -### phys_in_use register +### dual_phy register - Absolute Address: 0x20 - Base Offset: 0x20 - Size: 0x4 -

Select whether one or both PHY lanes are active.

- -|Bits|Identifier|Access|Reset|Name| -|----|----------|------|-----|----| -| 0 | value | rw | 0x1 | — | - -### which_phy register - -- Absolute Address: 0x24 -- Base Offset: 0x24 -- Size: 0x4 - -

Select the PHY lane used when only one PHY is active.

+

Enable both PHY lanes; when clear, only PHY lane 0 is active.

|Bits|Identifier|Access|Reset|Name| |----|----------|------|-----|----| @@ -302,6 +291,38 @@ Don't override. Generated from: hyperbus_cfg_regs |-----|----------|------|-----|----| |31:22| value | rw | 0x4 | — | +### flush register + +- Absolute Address: 0x50 +- Base Offset: 0x50 +- Size: 0x4 + +

Write to stop accepting requests and drain all in-flight HyperBus operations.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 0 | request | w | — | — | + +#### request field + +

Writing any value executes the ordering barrier; the value is not stored.

+ +### status register + +- Absolute Address: 0x54 +- Base Offset: 0x54 +- Size: 0x4 + +

Sticky protocol and address error status.

+ +|Bits| Identifier | Access |Reset|Name| +|----|------------|---------|-----|----| +| 0 |decode_error|rw, woclr| 0x0 | — | + +#### decode_error field + +

A host request did not match any configured chip address range.

+ ### chip4_base register - Absolute Address: 0x58 diff --git a/include/hyperbus/typedef.svh b/include/hyperbus/typedef.svh new file mode 100644 index 0000000..29193bf --- /dev/null +++ b/include/hyperbus/typedef.svh @@ -0,0 +1,47 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +`ifndef HYPERBUS_TYPEDEF_SVH_ +`define HYPERBUS_TYPEDEF_SVH_ + +`define HYPERBUS_TYPEDEF_HOST_REQ_T(__name, __addr_t) \ + typedef struct packed { \ + logic write; \ + __addr_t addr; \ + hyperbus_pkg::hyper_blen_t beats; \ + hyperbus_pkg::hyper_host_size_t size; \ + hyperbus_pkg::hyper_host_burst_e burst; \ + hyperbus_pkg::hyper_atomic_op_e atomic_op; \ + logic atomic_return; \ + logic ordered; \ + } __name; + +`define HYPERBUS_TYPEDEF_HOST_W_T(__name, __data_t, __strb_t) \ + typedef struct packed { \ + __data_t data; \ + __strb_t strb; \ + logic last; \ + } __name; + +`define HYPERBUS_TYPEDEF_HOST_R_T(__name, __data_t) \ + typedef struct packed { \ + __data_t data; \ + hyperbus_pkg::hyper_resp_e resp; \ + logic last; \ + logic atomic_ok; \ + } __name; + +`define HYPERBUS_TYPEDEF_HOST_WRSP_T(__name) \ + typedef struct packed { \ + hyperbus_pkg::hyper_resp_e resp; \ + logic atomic_ok; \ + } __name; + +`define HYPERBUS_TYPEDEF_HOST_ALL_CT(__name, __addr_t, __data_t, __strb_t) \ + `HYPERBUS_TYPEDEF_HOST_REQ_T(__name``_req_t, __addr_t) \ + `HYPERBUS_TYPEDEF_HOST_W_T(__name``_w_t, __data_t, __strb_t) \ + `HYPERBUS_TYPEDEF_HOST_R_T(__name``_r_t, __data_t) \ + `HYPERBUS_TYPEDEF_HOST_WRSP_T(__name``_wrsp_t) + +`endif diff --git a/include/hyperbus_cfg_regs.h b/include/hyperbus_cfg_regs.h index a5a09ba..80dd478 100644 --- a/include/hyperbus_cfg_regs.h +++ b/include/hyperbus_cfg_regs.h @@ -66,17 +66,11 @@ extern "C" { #define HYPERBUS_CFG_REGS__ADDRESS_SPACE__VALUE_bw 1 #define HYPERBUS_CFG_REGS__ADDRESS_SPACE__VALUE_reset 0x0 -// reg - hyperbus_cfg_regs::phys_in_use -#define HYPERBUS_CFG_REGS__PHYS_IN_USE__VALUE_bm 0x1 -#define HYPERBUS_CFG_REGS__PHYS_IN_USE__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__PHYS_IN_USE__VALUE_bw 1 -#define HYPERBUS_CFG_REGS__PHYS_IN_USE__VALUE_reset 0x1 - -// reg - hyperbus_cfg_regs::which_phy -#define HYPERBUS_CFG_REGS__WHICH_PHY__VALUE_bm 0x1 -#define HYPERBUS_CFG_REGS__WHICH_PHY__VALUE_bp 0 -#define HYPERBUS_CFG_REGS__WHICH_PHY__VALUE_bw 1 -#define HYPERBUS_CFG_REGS__WHICH_PHY__VALUE_reset 0x1 +// reg - hyperbus_cfg_regs::dual_phy +#define HYPERBUS_CFG_REGS__DUAL_PHY__VALUE_bm 0x1 +#define HYPERBUS_CFG_REGS__DUAL_PHY__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__DUAL_PHY__VALUE_bw 1 +#define HYPERBUS_CFG_REGS__DUAL_PHY__VALUE_reset 0x1 // reg - hyperbus_cfg_regs::t_csh_cycles #define HYPERBUS_CFG_REGS__T_CSH_CYCLES__VALUE_bm 0xf @@ -138,6 +132,17 @@ extern "C" { #define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_bw 10 #define HYPERBUS_CFG_REGS__CHIP3_BOUND__VALUE_reset 0x4 +// reg - hyperbus_cfg_regs::flush +#define HYPERBUS_CFG_REGS__FLUSH__REQUEST_bm 0x1 +#define HYPERBUS_CFG_REGS__FLUSH__REQUEST_bp 0 +#define HYPERBUS_CFG_REGS__FLUSH__REQUEST_bw 1 + +// reg - hyperbus_cfg_regs::status +#define HYPERBUS_CFG_REGS__STATUS__DECODE_ERROR_bm 0x1 +#define HYPERBUS_CFG_REGS__STATUS__DECODE_ERROR_bp 0 +#define HYPERBUS_CFG_REGS__STATUS__DECODE_ERROR_bw 1 +#define HYPERBUS_CFG_REGS__STATUS__DECODE_ERROR_reset 0x0 + // reg - hyperbus_cfg_regs::chip4_base #define HYPERBUS_CFG_REGS__CHIP4_BASE__VALUE_bm 0xffc00000 #define HYPERBUS_CFG_REGS__CHIP4_BASE__VALUE_bp 22 @@ -196,8 +201,8 @@ typedef struct __attribute__ ((__packed__)) { uint32_t t_tx_clk_delay; uint32_t address_mask_msb; uint32_t address_space; - uint32_t phys_in_use; - uint32_t which_phy; + uint32_t dual_phy; + uint8_t RESERVED_24_27[0x4]; uint32_t t_csh_cycles; uint32_t csn_to_ck_cycles; uint32_t chip0_base; @@ -208,7 +213,8 @@ typedef struct __attribute__ ((__packed__)) { uint32_t chip2_bound; uint32_t chip3_base; uint32_t chip3_bound; - uint8_t RESERVED_50_57[0x8]; + uint32_t flush; + uint32_t status; uint32_t chip4_base; uint32_t chip4_bound; uint32_t chip5_base; diff --git a/scripts/start.tcl b/scripts/start.tcl index 92aeeb9..e1256cc 100644 --- a/scripts/start.tcl +++ b/scripts/start.tcl @@ -15,7 +15,8 @@ proc run_test {top_name wlf_name} { file delete -force $transcript_name transcript file $transcript_name - vsim $top_name -wlf $wlf_name -t 1ps -voptargs=+acc -classdebug + # Questa 10.7 rejects enum assignments in common_cells clearable CDC models. + vsim $top_name -wlf $wlf_name -t 1ps -voptargs=+acc -classdebug -suppress 8386 onfinish stop set StdArithNoWarnings 1 @@ -36,9 +37,10 @@ proc run_test {top_name wlf_name} { } } -run_test axi_hyper_tb_isochronous sim_run_isochronous.wlf -run_test axi_hyper_tb_synchronous sim_run_synchronous.wlf -run_test axi_hyper_tb_asynchronous sim_run_asynchronous.wlf -run_test hyperbus_cfg_regs_tb sim_run_cfg_regs.wlf +run_test axi_hyper_tb_isochronous sim_run_isochronous.wlf +run_test axi_hyper_tb_synchronous sim_run_synchronous.wlf +run_test axi_hyper_tb_asynchronous sim_run_asynchronous.wlf +run_test axi_hyper_tb_synchronous_one_phy sim_run_synchronous_one_phy.wlf +run_test hyperbus_cfg_regs_tb sim_run_cfg_regs.wlf quit -code $regression_failed -f diff --git a/src/hyperbus_async_bridge.sv b/src/hyperbus_async_bridge.sv index 120cec3..3742701 100644 --- a/src/hyperbus_async_bridge.sv +++ b/src/hyperbus_async_bridge.sv @@ -2,21 +2,26 @@ // Solderpad Hardware License, Version 0.51, see LICENSE for details. // SPDX-License-Identifier: SHL-0.51 +`include "common_cells/assertions.svh" +`include "common_cells/registers.svh" + module hyperbus_async_bridge #( parameter int unsigned RxFifoLogDepth = 3, parameter int unsigned TxFifoLogDepth = 3, + parameter int unsigned CdcSyncStages = 3, parameter type hyper_rx_t = logic, parameter type hyper_tx_t = logic, - parameter type tf_cdc_t = logic + parameter type hyper_cmd_t = logic ) ( input logic clk_sys_i, input logic rst_sys_ni, input logic clk_phy_i, input logic rst_phy_ni, - input hyperbus_pkg::hyper_cfg_t cfg_apply_i, + input hyperbus_pkg::phy_cfg_t cfg_apply_i, input logic frontend_cfg_apply_valid_i, output logic frontend_cfg_apply_ready_o, + output logic frontend_cfg_apply_done_o, output hyper_rx_t frontend_rx_o, output logic frontend_rx_valid_o, @@ -24,12 +29,12 @@ module hyperbus_async_bridge #( input hyper_tx_t frontend_tx_i, input logic frontend_tx_valid_i, output logic frontend_tx_ready_o, - output logic frontend_b_error_o, - output logic frontend_b_valid_o, - input logic frontend_b_ready_i, - input tf_cdc_t frontend_trans_i, - input logic frontend_trans_valid_i, - output logic frontend_trans_ready_o, + output logic frontend_wrsp_error_o, + output logic frontend_wrsp_valid_o, + input logic frontend_wrsp_ready_i, + input hyper_cmd_t frontend_cmd_i, + input logic frontend_cmd_valid_i, + output logic frontend_cmd_ready_o, input hyper_rx_t backend_rx_i, input logic backend_rx_valid_i, @@ -37,93 +42,143 @@ module hyperbus_async_bridge #( output hyper_tx_t backend_tx_o, output logic backend_tx_valid_o, input logic backend_tx_ready_i, - input logic backend_b_error_i, - input logic backend_b_valid_i, - output logic backend_b_ready_o, - output tf_cdc_t backend_trans_o, - output logic backend_trans_valid_o, - input logic backend_trans_ready_i, - - output hyperbus_pkg::hyper_cfg_t cfg_apply_o, + input logic backend_wrsp_error_i, + input logic backend_wrsp_valid_i, + output logic backend_wrsp_ready_o, + output hyper_cmd_t backend_cmd_o, + output logic backend_cmd_valid_o, + input logic backend_cmd_ready_i, + + output hyperbus_pkg::phy_cfg_t cfg_apply_o, output logic cfg_apply_valid_o, input logic cfg_apply_ready_i ); - cdc_2phase #( - .T ( hyperbus_pkg::hyper_cfg_t ) - ) i_cdc_2phase_cfg ( - .src_rst_ni ( rst_sys_ni ), - .src_clk_i ( clk_sys_i ), - .src_data_i ( cfg_apply_i ), - .src_valid_i ( frontend_cfg_apply_valid_i ), - .src_ready_o ( frontend_cfg_apply_ready_o ), - .dst_rst_ni ( rst_phy_ni ), - .dst_clk_i ( clk_phy_i ), - .dst_data_o ( cfg_apply_o ), - .dst_valid_o ( cfg_apply_valid_o ), - .dst_ready_i ( cfg_apply_ready_i ) + logic cfg_apply_src_fire; + logic cfg_apply_pending_d, cfg_apply_pending_q; + + `ASSERT_INIT(CdcSyncStagesValid, CdcSyncStages >= 3) + // Preserve FIFO elasticity beyond the round-trip pointer synchronization latency. + `ASSERT_INIT(TxCdcFifoDepthValid, (1 << TxFifoLogDepth) > (2 * CdcSyncStages)) + `ASSERT_INIT(RxCdcFifoDepthValid, (1 << RxFifoLogDepth) > (2 * CdcSyncStages)) + + assign cfg_apply_src_fire = + frontend_cfg_apply_valid_i && frontend_cfg_apply_ready_o; + assign frontend_cfg_apply_done_o = + cfg_apply_pending_q && frontend_cfg_apply_ready_o; + + always_comb begin + cfg_apply_pending_d = cfg_apply_pending_q; + + if (cfg_apply_src_fire) begin + cfg_apply_pending_d = 1'b1; + end else if (frontend_cfg_apply_done_o) begin + cfg_apply_pending_d = 1'b0; + end + end + + `FFARN(cfg_apply_pending_q, cfg_apply_pending_d, 1'b0, clk_sys_i, rst_sys_ni) + + cdc_2phase_clearable #( + .T ( hyperbus_pkg::phy_cfg_t ), + .SYNC_STAGES ( CdcSyncStages ) + ) i_cdc_cfg ( + .src_rst_ni ( rst_sys_ni ), + .src_clk_i ( clk_sys_i ), + .src_clear_i ( 1'b0 ), + .src_clear_pending_o ( ), + .src_data_i ( cfg_apply_i ), + .src_valid_i ( frontend_cfg_apply_valid_i ), + .src_ready_o ( frontend_cfg_apply_ready_o ), + .dst_rst_ni ( rst_phy_ni ), + .dst_clk_i ( clk_phy_i ), + .dst_clear_i ( 1'b0 ), + .dst_clear_pending_o ( ), + .dst_data_o ( cfg_apply_o ), + .dst_valid_o ( cfg_apply_valid_o ), + .dst_ready_i ( cfg_apply_ready_i ) ); - cdc_2phase #( - .T ( tf_cdc_t ) - ) i_cdc_2phase_trans ( - .src_rst_ni ( rst_sys_ni ), - .src_clk_i ( clk_sys_i ), - .src_data_i ( frontend_trans_i ), - .src_valid_i ( frontend_trans_valid_i ), - .src_ready_o ( frontend_trans_ready_o ), - .dst_rst_ni ( rst_phy_ni ), - .dst_clk_i ( clk_phy_i ), - .dst_data_o ( backend_trans_o ), - .dst_valid_o ( backend_trans_valid_o ), - .dst_ready_i ( backend_trans_ready_i ) + cdc_2phase_clearable #( + .T ( hyper_cmd_t ), + .SYNC_STAGES ( CdcSyncStages ) + ) i_cdc_cmd ( + .src_rst_ni ( rst_sys_ni ), + .src_clk_i ( clk_sys_i ), + .src_clear_i ( 1'b0 ), + .src_clear_pending_o ( ), + .src_data_i ( frontend_cmd_i ), + .src_valid_i ( frontend_cmd_valid_i ), + .src_ready_o ( frontend_cmd_ready_o ), + .dst_rst_ni ( rst_phy_ni ), + .dst_clk_i ( clk_phy_i ), + .dst_clear_i ( 1'b0 ), + .dst_clear_pending_o ( ), + .dst_data_o ( backend_cmd_o ), + .dst_valid_o ( backend_cmd_valid_o ), + .dst_ready_i ( backend_cmd_ready_i ) ); - cdc_2phase #( - .T ( logic ) - ) i_cdc_2phase_b ( - .src_rst_ni ( rst_phy_ni ), - .src_clk_i ( clk_phy_i ), - .src_data_i ( backend_b_error_i ), - .src_valid_i ( backend_b_valid_i ), - .src_ready_o ( backend_b_ready_o ), - .dst_rst_ni ( rst_sys_ni ), - .dst_clk_i ( clk_sys_i ), - .dst_data_o ( frontend_b_error_o ), - .dst_valid_o ( frontend_b_valid_o ), - .dst_ready_i ( frontend_b_ready_i ) + cdc_2phase_clearable #( + .T ( logic ), + .SYNC_STAGES ( CdcSyncStages ) + ) i_cdc_wrsp ( + .src_rst_ni ( rst_phy_ni ), + .src_clk_i ( clk_phy_i ), + .src_clear_i ( 1'b0 ), + .src_clear_pending_o ( ), + .src_data_i ( backend_wrsp_error_i ), + .src_valid_i ( backend_wrsp_valid_i ), + .src_ready_o ( backend_wrsp_ready_o ), + .dst_rst_ni ( rst_sys_ni ), + .dst_clk_i ( clk_sys_i ), + .dst_clear_i ( 1'b0 ), + .dst_clear_pending_o ( ), + .dst_data_o ( frontend_wrsp_error_o ), + .dst_valid_o ( frontend_wrsp_valid_o ), + .dst_ready_i ( frontend_wrsp_ready_i ) ); - cdc_fifo_gray #( - .T ( hyper_tx_t ), - .LOG_DEPTH ( TxFifoLogDepth ) + cdc_fifo_gray_clearable #( + .T ( hyper_tx_t ), + .LOG_DEPTH ( TxFifoLogDepth ), + .SYNC_STAGES ( CdcSyncStages ) ) i_cdc_fifo_tx ( - .src_rst_ni ( rst_sys_ni ), - .src_clk_i ( clk_sys_i ), - .src_data_i ( frontend_tx_i ), - .src_valid_i ( frontend_tx_valid_i ), - .src_ready_o ( frontend_tx_ready_o ), - .dst_rst_ni ( rst_phy_ni ), - .dst_clk_i ( clk_phy_i ), - .dst_data_o ( backend_tx_o ), - .dst_valid_o ( backend_tx_valid_o ), - .dst_ready_i ( backend_tx_ready_i ) + .src_rst_ni ( rst_sys_ni ), + .src_clk_i ( clk_sys_i ), + .src_clear_i ( 1'b0 ), + .src_clear_pending_o ( ), + .src_data_i ( frontend_tx_i ), + .src_valid_i ( frontend_tx_valid_i ), + .src_ready_o ( frontend_tx_ready_o ), + .dst_rst_ni ( rst_phy_ni ), + .dst_clk_i ( clk_phy_i ), + .dst_clear_i ( 1'b0 ), + .dst_clear_pending_o ( ), + .dst_data_o ( backend_tx_o ), + .dst_valid_o ( backend_tx_valid_o ), + .dst_ready_i ( backend_tx_ready_i ) ); - cdc_fifo_gray #( - .T ( hyper_rx_t ), - .LOG_DEPTH ( RxFifoLogDepth ) + cdc_fifo_gray_clearable #( + .T ( hyper_rx_t ), + .LOG_DEPTH ( RxFifoLogDepth ), + .SYNC_STAGES ( CdcSyncStages ) ) i_cdc_fifo_rx ( - .src_rst_ni ( rst_phy_ni ), - .src_clk_i ( clk_phy_i ), - .src_data_i ( backend_rx_i ), - .src_valid_i ( backend_rx_valid_i ), - .src_ready_o ( backend_rx_ready_o ), - .dst_rst_ni ( rst_sys_ni ), - .dst_clk_i ( clk_sys_i ), - .dst_data_o ( frontend_rx_o ), - .dst_valid_o ( frontend_rx_valid_o ), - .dst_ready_i ( frontend_rx_ready_i ) + .src_rst_ni ( rst_phy_ni ), + .src_clk_i ( clk_phy_i ), + .src_clear_i ( 1'b0 ), + .src_clear_pending_o ( ), + .src_data_i ( backend_rx_i ), + .src_valid_i ( backend_rx_valid_i ), + .src_ready_o ( backend_rx_ready_o ), + .dst_rst_ni ( rst_sys_ni ), + .dst_clk_i ( clk_sys_i ), + .dst_clear_i ( 1'b0 ), + .dst_clear_pending_o ( ), + .dst_data_o ( frontend_rx_o ), + .dst_valid_o ( frontend_rx_valid_o ), + .dst_ready_i ( frontend_rx_ready_i ) ); endmodule diff --git a/src/hyperbus_asynchronous.sv b/src/hyperbus_asynchronous.sv index 3e5c763..f332340 100644 --- a/src/hyperbus_asynchronous.sv +++ b/src/hyperbus_asynchronous.sv @@ -2,6 +2,9 @@ // Solderpad Hardware License, Version 0.51, see LICENSE for details. // SPDX-License-Identifier: SHL-0.51 +`include "hyperbus/typedef.svh" +`include "common_cells/assertions.svh" + module hyperbus_asynchronous #( parameter int unsigned NumChips = -1, parameter int unsigned NumPhys = 2, @@ -11,24 +14,15 @@ module hyperbus_asynchronous #( parameter int unsigned AxiUserWidth = -1, parameter type axi_req_t = logic, parameter type axi_rsp_t = logic, - parameter type axi_w_chan_t = logic, - parameter type axi_b_chan_t = logic, - parameter type axi_ar_chan_t = logic, - parameter type axi_r_chan_t = logic, - parameter type axi_aw_chan_t = logic, - parameter int unsigned RegAddrWidth = -1, parameter int unsigned RegDataWidth = -1, - parameter int unsigned MinFreqMHz = 100, parameter type reg_req_t = logic, parameter type reg_rsp_t = logic, parameter type axi_rule_t = logic, parameter int unsigned RxFifoLogDepth = 3, parameter int unsigned TxFifoLogDepth = 3, - parameter logic [RegDataWidth-1:0] RstChipBase = 'h0, - parameter logic [RegDataWidth-1:0] RstChipSpace = 'h1_0000, - parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys, MinFreqMHz), parameter int unsigned PhyStartupCycles = 300 * 200, - parameter int unsigned SyncStages = 2 + parameter int unsigned SyncStages = 2, + parameter int unsigned CdcSyncStages = 3 ) ( input logic clk_sys_i, input logic rst_sys_ni, @@ -57,6 +51,8 @@ module hyperbus_asynchronous #( output logic [NumPhys-1:0] hyper_reset_no ); + `ASSERT_INIT(AxiAddrWidthValid, AxiAddrWidth >= $clog2(AxiDataWidth / 8)) + typedef struct packed { logic [(16*NumPhys)-1:0] data; logic last; @@ -72,31 +68,57 @@ module hyperbus_asynchronous #( typedef struct packed { hyperbus_pkg::hyper_tf_t trans; logic [NumChips-1:0] cs; - } tf_cdc_t; + } hyper_cmd_t; + + typedef logic [AxiAddrWidth-1:0] host_addr_t; + typedef logic [AxiDataWidth-1:0] host_data_t; + typedef logic [AxiDataWidth/8-1:0] host_strb_t; + `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) logic clk_backend; logic clk_backend_90; logic rst_backend_n; logic [7:0] backend_tx_clk_delay; - hyperbus_pkg::hyper_cfg_t cfg_frontend_apply; - logic cfg_frontend_apply_valid; - logic cfg_frontend_apply_ready; - hyperbus_pkg::hyper_cfg_t cfg_backend_apply; - logic cfg_backend_apply_valid; - logic cfg_backend_apply_ready; + hyperbus_pkg::phy_cfg_t frontend_cfg_apply; + logic frontend_cfg_apply_valid; + logic frontend_cfg_apply_ready; + logic frontend_cfg_apply_done; + logic frontend_drain; + logic host_idle; + hyperbus_pkg::phy_cfg_t backend_cfg_apply; + logic backend_cfg_apply_valid; + logic backend_cfg_apply_ready; - hyper_rx_t frontend_rx; - logic frontend_rx_valid; - logic frontend_rx_ready; - hyper_tx_t frontend_tx; - logic frontend_tx_valid; - logic frontend_tx_ready; - logic frontend_b_error; - logic frontend_b_valid; - logic frontend_b_ready; - tf_cdc_t frontend_trans; - logic frontend_trans_valid; - logic frontend_trans_ready; + hyperbus_pkg::frontend_cfg_t frontend_cfg; + axi_rule_t [NumChips-1:0] frontend_chip_rules; + + host_req_t host_req; + logic host_req_valid; + logic host_req_ready; + host_w_t host_w; + logic host_w_valid; + logic host_w_ready; + host_r_t host_r; + logic host_r_valid; + logic host_r_ready; + host_wrsp_t host_wrsp; + logic host_wrsp_valid; + logic host_wrsp_ready; + + hyper_rx_t midend_rx; + logic midend_rx_valid; + logic midend_rx_ready; + hyper_tx_t midend_tx; + logic midend_tx_valid; + logic midend_tx_ready; + logic midend_wrsp_error; + logic midend_wrsp_valid; + logic midend_wrsp_ready; + hyper_cmd_t midend_cmd; + logic midend_cmd_valid; + logic midend_cmd_ready; + logic midend_trans_active; + logic midend_decode_error; hyper_rx_t backend_rx; logic backend_rx_valid; @@ -104,14 +126,18 @@ module hyperbus_asynchronous #( hyper_tx_t backend_tx; logic backend_tx_valid; logic backend_tx_ready; - logic backend_b_error; - logic backend_b_valid; - logic backend_b_ready; - tf_cdc_t backend_trans; - logic backend_trans_valid; - logic backend_trans_ready; + logic backend_wrsp_error; + logic backend_wrsp_valid; + logic backend_wrsp_ready; + hyper_cmd_t backend_cmd; + logic backend_cmd_valid; + logic backend_cmd_ready; logic rst_phy_async_n; + + // Clearable CDCs permit an independent PHY reset without creating phantom + // transfers. Such a reset can discard a pending transfer and is therefore + // only a supported software operation while the controller is idle. assign rst_phy_async_n = rst_sys_ni & rst_phy_ni; assign clk_backend = clk_phy_i; @@ -135,92 +161,151 @@ module hyperbus_asynchronous #( .out_o ( clk_backend_90 ) ); - hyperbus_frontend #( + hyperbus_cfg_frontend #( .NumChips ( NumChips ), .NumPhys ( NumPhys ), - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidth ( AxiIdWidth ), - .AxiUserWidth ( AxiUserWidth ), - .axi_req_t ( axi_req_t ), - .axi_rsp_t ( axi_rsp_t ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), - .axi_rule_t ( axi_rule_t ), - .hyper_rx_t ( hyper_rx_t ), - .hyper_tx_t ( hyper_tx_t ), - .tf_cdc_t ( tf_cdc_t ), - .RegAddrWidth ( RegAddrWidth ), - .RegDataWidth ( RegDataWidth ), - .RstChipBase ( RstChipBase ), - .RstChipSpace ( RstChipSpace ), - .RstCfg ( RstCfg ) - ) i_frontend ( + .host_rule_t ( axi_rule_t ), + .RegDataWidth ( RegDataWidth ) + ) i_cfg_frontend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), - .axi_req_i ( axi_req_i ), - .axi_rsp_o ( axi_rsp_o ), .reg_req_i ( reg_req_i ), .reg_rsp_o ( reg_rsp_o ), - .cfg_o ( ), - .cfg_apply_o ( cfg_frontend_apply ), - .cfg_apply_valid_o ( cfg_frontend_apply_valid ), - .cfg_apply_ready_i ( cfg_frontend_apply_ready ), - .rx_i ( frontend_rx ), - .rx_valid_i ( frontend_rx_valid ), - .rx_ready_o ( frontend_rx_ready ), - .tx_o ( frontend_tx ), - .tx_valid_o ( frontend_tx_valid ), - .tx_ready_i ( frontend_tx_ready ), - .b_error_i ( frontend_b_error ), - .b_valid_i ( frontend_b_valid ), - .b_ready_o ( frontend_b_ready ), - .trans_o ( frontend_trans ), - .trans_valid_o ( frontend_trans_valid ), - .trans_ready_i ( frontend_trans_ready ) + .drain_o ( frontend_drain ), + .host_idle_i ( host_idle ), + .trans_active_i ( midend_trans_active ), + .cfg_apply_o ( frontend_cfg_apply ), + .cfg_apply_valid_o ( frontend_cfg_apply_valid ), + .cfg_apply_ready_i ( frontend_cfg_apply_ready ), + .cfg_apply_done_i ( frontend_cfg_apply_done ), + .frontend_cfg_o ( frontend_cfg ), + .chip_rules_o ( frontend_chip_rules ), + .decode_error_i ( midend_decode_error ) + ); + + hyperbus_axi_frontend #( + .AxiDataWidth ( AxiDataWidth ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .host_req_t ( host_req_t ), + .host_w_t ( host_w_t ), + .host_r_t ( host_r_t ), + .host_wrsp_t ( host_wrsp_t ) + ) i_axi_frontend ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .drain_i ( frontend_drain ), + .idle_o ( host_idle ), + .axi_req_i ( axi_req_i ), + .axi_rsp_o ( axi_rsp_o ), + .host_req_o ( host_req ), + .host_req_valid_o ( host_req_valid ), + .host_req_ready_i ( host_req_ready ), + .host_w_o ( host_w ), + .host_w_valid_o ( host_w_valid ), + .host_w_ready_i ( host_w_ready ), + .host_r_i ( host_r ), + .host_r_valid_i ( host_r_valid ), + .host_r_ready_o ( host_r_ready ), + .host_wrsp_i ( host_wrsp ), + .host_wrsp_valid_i ( host_wrsp_valid ), + .host_wrsp_ready_o ( host_wrsp_ready ) + ); + + hyperbus_midend #( + .HostAddrWidth ( AxiAddrWidth ), + .HostDataWidth ( AxiDataWidth ), + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .host_req_t ( host_req_t ), + .host_w_t ( host_w_t ), + .host_r_t ( host_r_t ), + .host_wrsp_t ( host_wrsp_t ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .hyper_cmd_t ( hyper_cmd_t ), + .rule_t ( axi_rule_t ) + ) i_midend ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .host_req_i ( host_req ), + .host_req_valid_i ( host_req_valid ), + .host_req_ready_o ( host_req_ready ), + .host_w_i ( host_w ), + .host_w_valid_i ( host_w_valid ), + .host_w_ready_o ( host_w_ready ), + .host_r_o ( host_r ), + .host_r_valid_o ( host_r_valid ), + .host_r_ready_i ( host_r_ready ), + .host_wrsp_o ( host_wrsp ), + .host_wrsp_valid_o ( host_wrsp_valid ), + .host_wrsp_ready_i ( host_wrsp_ready ), + .frontend_cfg_i ( frontend_cfg ), + .chip_rules_i ( frontend_chip_rules ), + .trans_active_o ( midend_trans_active ), + .decode_error_o ( midend_decode_error ), + .rx_i ( midend_rx ), + .rx_valid_i ( midend_rx_valid ), + .rx_ready_o ( midend_rx_ready ), + .tx_o ( midend_tx ), + .tx_valid_o ( midend_tx_valid ), + .tx_ready_i ( midend_tx_ready ), + .wrsp_error_i ( midend_wrsp_error ), + .wrsp_valid_i ( midend_wrsp_valid ), + .wrsp_ready_o ( midend_wrsp_ready ), + .cmd_o ( midend_cmd ), + .cmd_valid_o ( midend_cmd_valid ), + .cmd_ready_i ( midend_cmd_ready ) ); hyperbus_async_bridge #( .RxFifoLogDepth ( RxFifoLogDepth ), .TxFifoLogDepth ( TxFifoLogDepth ), + .CdcSyncStages ( CdcSyncStages ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), - .tf_cdc_t ( tf_cdc_t ) + .hyper_cmd_t ( hyper_cmd_t ) ) i_bridge ( .clk_sys_i ( clk_sys_i ), .rst_sys_ni ( rst_sys_ni ), .clk_phy_i ( clk_backend ), .rst_phy_ni ( rst_backend_n ), - .cfg_apply_i ( cfg_frontend_apply ), - .frontend_cfg_apply_valid_i ( cfg_frontend_apply_valid ), - .frontend_cfg_apply_ready_o ( cfg_frontend_apply_ready ), - .frontend_rx_o ( frontend_rx ), - .frontend_rx_valid_o ( frontend_rx_valid ), - .frontend_rx_ready_i ( frontend_rx_ready ), - .frontend_tx_i ( frontend_tx ), - .frontend_tx_valid_i ( frontend_tx_valid ), - .frontend_tx_ready_o ( frontend_tx_ready ), - .frontend_b_error_o ( frontend_b_error ), - .frontend_b_valid_o ( frontend_b_valid ), - .frontend_b_ready_i ( frontend_b_ready ), - .frontend_trans_i ( frontend_trans ), - .frontend_trans_valid_i ( frontend_trans_valid ), - .frontend_trans_ready_o ( frontend_trans_ready ), + .cfg_apply_i ( frontend_cfg_apply ), + .frontend_cfg_apply_valid_i ( frontend_cfg_apply_valid ), + .frontend_cfg_apply_ready_o ( frontend_cfg_apply_ready ), + .frontend_cfg_apply_done_o ( frontend_cfg_apply_done ), + .frontend_rx_o ( midend_rx ), + .frontend_rx_valid_o ( midend_rx_valid ), + .frontend_rx_ready_i ( midend_rx_ready ), + .frontend_tx_i ( midend_tx ), + .frontend_tx_valid_i ( midend_tx_valid ), + .frontend_tx_ready_o ( midend_tx_ready ), + .frontend_wrsp_error_o ( midend_wrsp_error ), + .frontend_wrsp_valid_o ( midend_wrsp_valid ), + .frontend_wrsp_ready_i ( midend_wrsp_ready ), + .frontend_cmd_i ( midend_cmd ), + .frontend_cmd_valid_i ( midend_cmd_valid ), + .frontend_cmd_ready_o ( midend_cmd_ready ), .backend_rx_i ( backend_rx ), .backend_rx_valid_i ( backend_rx_valid ), .backend_rx_ready_o ( backend_rx_ready ), .backend_tx_o ( backend_tx ), .backend_tx_valid_o ( backend_tx_valid ), .backend_tx_ready_i ( backend_tx_ready ), - .backend_b_error_i ( backend_b_error ), - .backend_b_valid_i ( backend_b_valid ), - .backend_b_ready_o ( backend_b_ready ), - .backend_trans_o ( backend_trans ), - .backend_trans_valid_o ( backend_trans_valid ), - .backend_trans_ready_i ( backend_trans_ready ), - .cfg_apply_o ( cfg_backend_apply ), - .cfg_apply_valid_o ( cfg_backend_apply_valid ), - .cfg_apply_ready_i ( cfg_backend_apply_ready ) + .backend_wrsp_error_i ( backend_wrsp_error ), + .backend_wrsp_valid_i ( backend_wrsp_valid ), + .backend_wrsp_ready_o ( backend_wrsp_ready ), + .backend_cmd_o ( backend_cmd ), + .backend_cmd_valid_o ( backend_cmd_valid ), + .backend_cmd_ready_i ( backend_cmd_ready ), + .cfg_apply_o ( backend_cfg_apply ), + .cfg_apply_valid_o ( backend_cfg_apply_valid ), + .cfg_apply_ready_i ( backend_cfg_apply_ready ) ); hyperbus_backend #( @@ -230,16 +315,15 @@ module hyperbus_asynchronous #( .SyncStages ( SyncStages ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), - .tf_cdc_t ( tf_cdc_t ), - .RstCfg ( RstCfg ) + .hyper_cmd_t ( hyper_cmd_t ) ) i_backend ( .clk_i ( clk_backend ), .clk_90_i ( clk_backend_90 ), .rst_ni ( rst_backend_n ), .test_mode_i ( test_mode_i ), - .cfg_apply_i ( cfg_backend_apply ), - .cfg_apply_valid_i ( cfg_backend_apply_valid ), - .cfg_apply_ready_o ( cfg_backend_apply_ready ), + .cfg_apply_i ( backend_cfg_apply ), + .cfg_apply_valid_i ( backend_cfg_apply_valid ), + .cfg_apply_ready_o ( backend_cfg_apply_ready ), .busy_o ( ), .tx_clk_delay_o ( backend_tx_clk_delay ), .rx_o ( backend_rx ), @@ -248,12 +332,12 @@ module hyperbus_asynchronous #( .tx_i ( backend_tx ), .tx_valid_i ( backend_tx_valid ), .tx_ready_o ( backend_tx_ready ), - .b_error_o ( backend_b_error ), - .b_valid_o ( backend_b_valid ), - .b_ready_i ( backend_b_ready ), - .trans_i ( backend_trans ), - .trans_valid_i ( backend_trans_valid ), - .trans_ready_o ( backend_trans_ready ), + .wrsp_error_o ( backend_wrsp_error ), + .wrsp_valid_o ( backend_wrsp_valid ), + .wrsp_ready_i ( backend_wrsp_ready ), + .cmd_i ( backend_cmd ), + .cmd_valid_i ( backend_cmd_valid ), + .cmd_ready_o ( backend_cmd_ready ), .hyper_cs_no ( hyper_cs_no ), .hyper_ck_o ( hyper_ck_o ), .hyper_ck_no ( hyper_ck_no ), @@ -266,4 +350,12 @@ module hyperbus_asynchronous #( .hyper_reset_no ( hyper_reset_no ) ); + // pragma translate_off + `ifndef VERILATOR + phy_reset_only_when_idle : assert property ( + @(posedge clk_sys_i) disable iff (!rst_sys_ni) !rst_phy_ni |-> !midend_trans_active + ) else $error("%m: PHY reset asserted while a HyperBus transaction is active"); + `endif + // pragma translate_on + endmodule diff --git a/src/hyperbus_axi.sv b/src/hyperbus_axi.sv index 5ccc40b..84f0b5d 100644 --- a/src/hyperbus_axi.sv +++ b/src/hyperbus_axi.sv @@ -1,170 +1,189 @@ // Copyright 2023 ETH Zurich and University of Bologna. // Solderpad Hardware License, Version 0.51, see LICENSE for details. // SPDX-License-Identifier: SHL-0.51 -// -// Thomas Benz -// Paul Scheffler `include "axi/typedef.svh" `include "common_cells/registers.svh" - -module hyperbus_axi #( - parameter int unsigned AxiDataWidth = -1, - parameter int unsigned AxiAddrWidth = -1, - parameter int unsigned AxiIdWidth = -1, - parameter int unsigned AxiUserWidth = -1, - parameter type axi_req_t = logic, - parameter type axi_rsp_t = logic, - parameter int unsigned NumChips = -1, - parameter int unsigned NumPhys = -1, - parameter type hyper_tx_t = logic, - parameter type hyper_rx_t = logic, - parameter type rule_t = logic +`include "common_cells/assertions.svh" + +module hyperbus_axi_frontend #( + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned AxiAddrWidth = -1, + parameter int unsigned AxiIdWidth = -1, + parameter int unsigned AxiUserWidth = -1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter type host_req_t = logic, + parameter type host_w_t = logic, + parameter type host_r_t = logic, + parameter type host_wrsp_t = logic ) ( - input logic clk_i, - input logic rst_ni, - // AXI port - input axi_req_t axi_req_i, - output axi_rsp_t axi_rsp_o, - // PHI port - input hyper_rx_t rx_i, - input logic rx_valid_i, - output logic rx_ready_o, - - output hyper_tx_t tx_o, - output logic tx_valid_o, - input logic tx_ready_i, - - input logic b_error_i, - input logic b_valid_i, - output logic b_ready_o, - - output hyperbus_pkg::hyper_tf_t trans_o, - output logic [NumChips-1:0] trans_cs_o, - output logic trans_valid_o, - input logic trans_ready_i, - - input hyperbus_pkg::frontend_cfg_t frontend_cfg_i, - input rule_t [NumChips-1:0] chip_rules_i, - output logic trans_active_o + input logic clk_i, + input logic rst_ni, + + input logic drain_i, + output logic idle_o, + + input axi_req_t axi_req_i, + output axi_rsp_t axi_rsp_o, + + output host_req_t host_req_o, + output logic host_req_valid_o, + input logic host_req_ready_i, + + output host_w_t host_w_o, + output logic host_w_valid_o, + input logic host_w_ready_i, + + input host_r_t host_r_i, + input logic host_r_valid_i, + output logic host_r_ready_o, + + input host_wrsp_t host_wrsp_i, + input logic host_wrsp_valid_i, + output logic host_wrsp_ready_o ); - localparam AxiDataBytes = AxiDataWidth/8; - localparam AxiBusAddrWidth = $clog2(AxiDataBytes); - localparam PhyDataWidth = NumPhys*16; - localparam PhyDataBytes = PhyDataWidth/8; - localparam ChipSelWidth = cf_math_pkg::idx_width(NumChips); - localparam ByteCntWidth = cf_math_pkg::idx_width(AxiDataBytes); + localparam int unsigned AxiDataBytes = AxiDataWidth / 8; + + `ASSERT_INIT(AxiAddrWidthValid, AxiAddrWidth >= 1) + `ASSERT_INIT(AxiDataWidthValid, + AxiDataWidth >= 16 && AxiDataWidth <= 1024 && + (AxiDataWidth & (AxiDataWidth - 1)) == 0) + `ASSERT_INIT(AxiIdWidthValid, AxiIdWidth >= 1) + `ASSERT_INIT(AxiUserWidthValid, AxiUserWidth >= 1) typedef logic [AxiAddrWidth-1:0] axi_addr_t; - typedef logic [ByteCntWidth-1:0] byte_cnt_t; - typedef logic [ByteCntWidth-3:0] word_cnt_t; typedef logic [AxiDataWidth-1:0] axi_data_t; - typedef logic [ChipSelWidth-1:0] chip_sel_idx_t; + typedef logic [AxiIdWidth-1:0] axi_id_t; + typedef logic [AxiDataBytes-1:0] axi_strb_t; + typedef logic [AxiUserWidth-1:0] axi_user_t; - `AXI_TYPEDEF_ALL_CT(axi_fifo,axi_fifo_req,axi_fifo_rsp,axi_addr_t,logic[AxiIdWidth-1:0],axi_data_t,logic[AxiDataBytes-1:0],logic[AxiUserWidth-1:0]) + `AXI_TYPEDEF_ALL_CT(axi_fifo, axi_fifo_req, axi_fifo_rsp, axi_addr_t, axi_id_t, + axi_data_t, axi_strb_t, axi_user_t) - // No need to track ID: serializer buffers it for us + // IDs stay in the AXI serializer; the neutral stream is single-outstanding. typedef struct packed { - axi_addr_t addr; - axi_pkg::len_t len; - axi_pkg::burst_t burst; - axi_pkg::size_t size; + axi_addr_t addr; + axi_pkg::len_t len; + axi_pkg::burst_t burst; + axi_pkg::size_t size; + axi_pkg::atop_t atop; } axi_ax_t; typedef struct packed { - axi_ax_t ax_data; - logic write; + axi_ax_t ax_data; + logic write; } ax_channel_spill_t; typedef struct packed { - logic valid; - axi_data_t data; - logic error; - logic last; - } axi_r_t; - - typedef struct packed { - logic [AxiDataWidth/8-1:0] strb; - axi_data_t data; - logic [AxiUserWidth-1:0] user; - logic last; + axi_strb_t strb; + axi_data_t data; + axi_user_t user; + logic last; } axi_w_chan_t; - typedef struct packed { - logic [7:0] data; - logic strb; - } axi_wbyte_t; - - // AXI FIFO downstream - axi_req_t fifo_out_req; - axi_rsp_t fifo_out_rsp; - - // Atomics Filter downstream - axi_req_t atop_out_req; - axi_rsp_t atop_out_rsp; - - // ID serializer downstream - axi_req_t ser_out_req; - axi_rsp_t ser_out_rsp; - axi_ax_t ser_out_req_aw; - axi_ax_t ser_out_req_ar; - - // AX arbiter downstream - axi_ax_t rr_out_req_ax; - logic rr_out_req_write; - logic spill_ax_valid, spill_ax_ready; - axi_ax_t spill_rr_out_req_ax; - logic spill_rr_out_req_write; - ax_channel_spill_t spill_ax_channel_in, spill_ax_channel_out; - - // AX handling - logic trans_handshake; - logic ax_valid, ax_ready; - axi_pkg::size_t ax_size_d, ax_size_q; - chip_sel_idx_t ax_chip_sel_idx; - hyperbus_pkg::hyper_blen_t ax_blen_postinc; - logic ax_blen_inc; - - // R channel - axi_r_t s_r_split; - - // R channel merge when phys_in_use != NumPhys - logic [PhyDataWidth-1:0] s_rx_data; - logic [PhyDataWidth/2-1:0] s_rx_data_lower_d, s_rx_data_lower_q; - logic s_rx_error; - logic s_rx_last; - logic s_rx_valid; - logic s_rx_ready; - logic merge_r_d, merge_r_q; - - // W channel - logic w_data_valid; - logic w_data_ready; + axi_req_t fifo_in_req; + axi_rsp_t fifo_in_rsp; + axi_req_t fifo_out_req; + axi_rsp_t fifo_out_rsp; + axi_req_t ser_out_req; + axi_rsp_t ser_out_rsp; + + axi_ax_t ser_out_req_aw; + axi_ax_t ser_out_req_ar; + axi_ax_t rr_out_req_ax; + axi_ax_t spill_rr_out_req_ax; + + ax_channel_spill_t spill_ax_channel_in; + ax_channel_spill_t spill_ax_channel_out; + axi_w_chan_t w_data_fifo; axi_w_chan_t w_data_fifo_in; - // W channel split when phys_in_use != NumPhys - logic [PhyDataWidth-1:0] s_tx_data; - logic [PhyDataBytes-1:0] s_tx_strb; - logic s_tx_last; - logic s_tx_valid; - logic s_tx_ready; - logic split_w_d, split_w_q; + logic spill_ax_valid; + logic spill_ax_ready; + logic spill_rr_out_req_write; + logic rr_out_req_write; + logic w_data_valid; + logic w_data_ready; - // Whether a transfer is currently active - logic trans_active_d, trans_active_q; - logic trans_active_set, trans_active_reset; - logic trans_wready_d, trans_wready_q; - logic trans_wready_set, trans_wready_reset; + localparam int unsigned PendingWidth = 8; + typedef logic [PendingWidth-1:0] pending_cnt_t; + typedef logic signed [PendingWidth-1:0] write_balance_t; + + pending_cnt_t read_pending_d, read_pending_q; + pending_cnt_t write_pending_d, write_pending_q; + write_balance_t write_balance_d, write_balance_q; + logic w_partial_d, w_partial_q; + logic allow_aw, allow_w; + logic axi_ar_fire, axi_aw_fire, axi_w_fire; + logic axi_atomic_r_fire; + logic axi_r_last_fire, axi_b_fire; + + always_comb begin : proc_axi_drain + allow_aw = !drain_i; + allow_w = !drain_i; + + if (drain_i) begin + // Complete only channel fragments accepted before the barrier. + allow_aw = !w_partial_q && (write_balance_q < 0); + allow_w = w_partial_q || (write_balance_q > 0); + end + + fifo_in_req = axi_req_i; + fifo_in_req.ar_valid = axi_req_i.ar_valid && !drain_i; + fifo_in_req.aw_valid = axi_req_i.aw_valid && allow_aw; + fifo_in_req.w_valid = axi_req_i.w_valid && allow_w; + + axi_rsp_o = fifo_in_rsp; + axi_rsp_o.ar_ready = fifo_in_rsp.ar_ready && !drain_i; + axi_rsp_o.aw_ready = fifo_in_rsp.aw_ready && allow_aw; + axi_rsp_o.w_ready = fifo_in_rsp.w_ready && allow_w; + end - logic [1:0] phys_in_use; + assign axi_ar_fire = axi_req_i.ar_valid && axi_rsp_o.ar_ready; + assign axi_aw_fire = axi_req_i.aw_valid && axi_rsp_o.aw_ready; + assign axi_w_fire = axi_req_i.w_valid && axi_rsp_o.w_ready; + assign axi_atomic_r_fire = axi_aw_fire && + axi_req_i.aw.atop[axi_pkg::ATOP_R_RESP]; + assign axi_r_last_fire = axi_rsp_o.r_valid && axi_req_i.r_ready && axi_rsp_o.r.last; + assign axi_b_fire = axi_rsp_o.b_valid && axi_req_i.b_ready; + + always_comb begin : proc_pending_counts + read_pending_d = read_pending_q; + write_pending_d = write_pending_q; + write_balance_d = write_balance_q; + w_partial_d = w_partial_q; + + read_pending_d = read_pending_q + pending_cnt_t'(axi_ar_fire) + + pending_cnt_t'(axi_atomic_r_fire) - + pending_cnt_t'(axi_r_last_fire); + + unique case ({axi_aw_fire, axi_b_fire}) + 2'b10: write_pending_d = write_pending_q + 1'b1; + 2'b01: write_pending_d = write_pending_q - 1'b1; + default:; + endcase + + if (axi_aw_fire) begin + write_balance_d = write_balance_d + 1'b1; + end + if (axi_w_fire && axi_req_i.w.last) begin + write_balance_d = write_balance_d - 1'b1; + end + if (axi_w_fire) begin + w_partial_d = !axi_req_i.w.last; + end + end - assign phys_in_use = (NumPhys==2) ? (frontend_cfg_i.phys_in_use + 1) : 1; + `FFARN(read_pending_q, read_pending_d, '0, clk_i, rst_ni) + `FFARN(write_pending_q, write_pending_d, '0, clk_i, rst_ni) + `FFARN(write_balance_q, write_balance_d, '0, clk_i, rst_ni) + `FFARN(w_partial_q, w_partial_d, 1'b0, clk_i, rst_ni) - // ============================ - // Serialize requests - // ============================ + assign idle_o = (read_pending_q == '0) && (write_pending_q == '0) && + (write_balance_q == '0) && !w_partial_q; axi_fifo #( .Depth ( 8 ), @@ -179,239 +198,119 @@ module hyperbus_axi #( ) i_axi_fifo ( .clk_i, .rst_ni, - .test_i ( 1'b0 ), - .slv_req_i ( axi_req_i ), - .slv_resp_o ( axi_rsp_o ), - .mst_req_o ( fifo_out_req ), - .mst_resp_i ( fifo_out_rsp ) - ); - - - // Block unsupported atomics - axi_atop_filter #( - .AxiIdWidth ( AxiIdWidth ), - .AxiMaxWriteTxns ( 1 ), - .axi_req_t ( axi_req_t ), - .axi_resp_t ( axi_rsp_t ) - ) i_axi_atop_filter ( - .clk_i, - .rst_ni, - .slv_req_i ( fifo_out_req ), - .slv_resp_o ( fifo_out_rsp ), - .mst_req_o ( atop_out_req ), - .mst_resp_i ( atop_out_rsp ) + .test_i ( 1'b0 ), + .slv_req_i ( fifo_in_req ), + .slv_resp_o ( fifo_in_rsp ), + .mst_req_o ( fifo_out_req ), + .mst_resp_i ( fifo_out_rsp ) ); - // Ensure we only handle one ID (master) at a time axi_serializer #( - .MaxReadTxns ( 1 ), - .MaxWriteTxns ( 1 ), - .AxiIdWidth ( AxiIdWidth ), - .axi_req_t ( axi_req_t ), - .axi_resp_t ( axi_rsp_t ) + .MaxReadTxns ( 1 ), + .MaxWriteTxns ( 1 ), + .AxiIdWidth ( AxiIdWidth ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_rsp_t ) ) i_axi_serializer ( .clk_i, .rst_ni, - .slv_req_i ( atop_out_req ), - .slv_resp_o ( atop_out_rsp ), - .mst_req_o ( ser_out_req ), - .mst_resp_i ( ser_out_rsp ) + .slv_req_i ( fifo_out_req ), + .slv_resp_o ( fifo_out_rsp ), + .mst_req_o ( ser_out_req ), + .mst_resp_i ( ser_out_rsp ) ); - // Round-robin-arbitrate between AR and AW channels (HyperBus is simplex) assign ser_out_req_ar.addr = ser_out_req.ar.addr; assign ser_out_req_ar.len = ser_out_req.ar.len; assign ser_out_req_ar.burst = ser_out_req.ar.burst; assign ser_out_req_ar.size = ser_out_req.ar.size; + assign ser_out_req_ar.atop = '0; assign ser_out_req_aw.addr = ser_out_req.aw.addr; assign ser_out_req_aw.len = ser_out_req.aw.len; assign ser_out_req_aw.burst = ser_out_req.aw.burst; assign ser_out_req_aw.size = ser_out_req.aw.size; + assign ser_out_req_aw.atop = ser_out_req.aw.atop; rr_arb_tree #( - .NumIn ( 2 ), - .DataType ( axi_ax_t ), - .AxiVldRdy ( 1 ), - .ExtPrio ( 1'b1 ) + .NumIn ( 2 ), + .DataType ( axi_ax_t ), + .AxiVldRdy ( 1 ), + .ExtPrio ( 1'b1 ) ) i_rr_arb_tree_ax ( .clk_i, .rst_ni, - .flush_i ( 1'b0 ), - .rr_i ( '0 ), - .req_i ( { ser_out_req.aw_valid, ser_out_req.ar_valid } ), - .gnt_o ( { ser_out_rsp.aw_ready, ser_out_rsp.ar_ready } ), - .data_i ( { ser_out_req_aw, ser_out_req_ar } ), - .req_o ( spill_ax_valid ), - .gnt_i ( spill_ax_ready ), - .data_o ( spill_rr_out_req_ax ), - .idx_o ( spill_rr_out_req_write ) + .flush_i ( 1'b0 ), + .rr_i ( '0 ), + .req_i ( {ser_out_req.aw_valid, ser_out_req.ar_valid} ), + .gnt_o ( {ser_out_rsp.aw_ready, ser_out_rsp.ar_ready} ), + .data_i ( {ser_out_req_aw, ser_out_req_ar} ), + .req_o ( spill_ax_valid ), + .gnt_i ( spill_ax_ready ), + .data_o ( spill_rr_out_req_ax ), + .idx_o ( spill_rr_out_req_write ) ); - // Cut paths between serializer and rr arb tree assign spill_ax_channel_in.ax_data = spill_rr_out_req_ax; - assign spill_ax_channel_in.write = spill_rr_out_req_write; + assign spill_ax_channel_in.write = spill_rr_out_req_write; spill_register #( - .T ( ax_channel_spill_t ) - ) ax_spill_register ( - .clk_i, - .rst_ni, - .valid_i (spill_ax_valid), - .ready_o (spill_ax_ready), - .data_i (spill_ax_channel_in), - .valid_o (ax_valid), - .ready_i (ax_ready), - .data_o (spill_ax_channel_out) - ); - - assign rr_out_req_ax = spill_ax_channel_out.ax_data; - assign rr_out_req_write = spill_ax_channel_out.write; - - assign trans_valid_o = ax_valid & ~trans_active_q; - assign ax_ready = trans_ready_i & ~trans_active_q; - - assign trans_handshake = trans_valid_o & trans_ready_i; - - // ============================ - // AX channel: handle - // ============================ - - // Handle address mapping to chip select - addr_decode #( - .NoIndices ( NumChips ), - .NoRules ( NumChips ), - .addr_t ( axi_addr_t ), - .rule_t ( rule_t ) - ) i_addr_decode_chip_sel ( - .addr_i ( rr_out_req_ax.addr ), - .addr_map_i ( chip_rules_i ), - .idx_o ( ax_chip_sel_idx ), - .dec_valid_o ( ), - .dec_error_o ( ), - .en_default_idx_i ( 1'b1 ), - .default_idx_i ( '0 ) - ); - - // Chip select binary to one hot decoding - always_comb begin : proc_comb_trans_cs - trans_cs_o = '0; - trans_cs_o[ax_chip_sel_idx] = 1'b1; - end - - // AX channel: forward, converting unmasked byte to masked word addresses - assign trans_o.write = rr_out_req_write; - assign trans_o.burst_type = 1'b1; // Wrapping bursts not (yet) supported - assign trans_o.address_space = frontend_cfg_i.address_space; - assign trans_o.address = (NumPhys == 2) ? - (frontend_cfg_i.phys_in_use ? - ((rr_out_req_ax.addr & ((32'b1 << frontend_cfg_i.address_mask_msb) - 1)) >> 2) : - (((rr_out_req_ax.addr & ((32'b1 << frontend_cfg_i.address_mask_msb) - 1)) >> 2) << 1)) : - ((rr_out_req_ax.addr & ((32'b1 << frontend_cfg_i.address_mask_msb) - 1)) >> 1); - - // Convert burst length from decremented, unaligned beats to non-decremented, aligned 16-bit words - always_comb begin - trans_o.burst= NumPhys; - ax_blen_inc = 1'b1; - if (rr_out_req_ax.size > NumPhys) begin - ax_blen_inc = 1'b1; - if( ((rr_out_req_ax.addr>>rr_out_req_ax.size)<>rr_out_req_ax.size)<> NumPhys ) + 1 ) << (NumPhys-1); - end else begin - trans_o.burst = ( ( ( rr_out_req_ax.addr[NumPhys-1:0] + (ax_blen_postinc<> NumPhys ) + 1 ) << (NumPhys-1); - end - end - end - end - - assign ax_blen_postinc = rr_out_req_ax.len + hyperbus_pkg::hyper_blen_t'(ax_blen_inc) ; - - // ============================ - // R channel - // ============================ - - assign ser_out_rsp.r.data = s_r_split.data; - assign ser_out_rsp.r.last = s_r_split.last; - assign ser_out_rsp.r.resp = s_r_split.error ? axi_pkg::RESP_SLVERR : axi_pkg::RESP_OKAY; - assign ser_out_rsp.r.id = '0; - assign ser_out_rsp.r.user = '0; - - always_comb begin - s_rx_valid = rx_valid_i; - rx_ready_o = s_rx_ready; - s_rx_data = rx_i.data; - s_rx_last = rx_i.last; - s_rx_error = rx_i.error; - s_rx_data_lower_d = s_rx_data_lower_q; - merge_r_d = merge_r_q; - if( (NumPhys==2) & (~frontend_cfg_i.phys_in_use) ) begin - if(rx_valid_i & s_rx_ready) begin - merge_r_d = merge_r_q + 1; - end - if(~frontend_cfg_i.which_phy) - s_rx_data = { rx_i.data[PhyDataWidth/2-1:0] , s_rx_data_lower_q }; - else - s_rx_data = { rx_i.data[PhyDataWidth-1:PhyDataWidth/2] , s_rx_data_lower_q }; - s_rx_valid = rx_valid_i & merge_r_q; - rx_ready_o = s_rx_ready; - s_rx_last = rx_i.last & merge_r_q; - s_rx_error = rx_i.error; - if(~merge_r_q) begin - if(~frontend_cfg_i.which_phy) - s_rx_data_lower_d = rx_i.data[PhyDataWidth/2-1:0]; - else - s_rx_data_lower_d = rx_i.data[PhyDataWidth-1:PhyDataWidth/2]; - end - end - end - - hyperbus_phy2r #( - .AxiDataWidth ( AxiDataWidth ), - .BurstLength ( hyperbus_pkg::HyperBurstWidth ), - .T ( axi_r_t ), - .NumPhys ( NumPhys ) - ) i_hyperbus_phy2r ( + .T ( ax_channel_spill_t ) + ) i_ax_spill_register ( .clk_i, .rst_ni, - .size ( rr_out_req_ax.size ), - .trans_handshake ( trans_handshake ), - .start_addr ( rr_out_req_ax.addr[AxiBusAddrWidth-1:0] ), - .burst_len ( ax_blen_postinc ), - .is_a_read ( !rr_out_req_write ), - .phy_valid_i ( s_rx_valid ), - .phy_ready_o ( s_rx_ready ), - .data_i ( s_rx_data ), - .last_i ( s_rx_last ), - .error_i ( s_rx_error ), - .axi_valid_o ( ser_out_rsp.r_valid ), - .axi_ready_i ( ser_out_req.r_ready ), - .data_o ( s_r_split ) + .valid_i ( spill_ax_valid ), + .ready_o ( spill_ax_ready ), + .data_i ( spill_ax_channel_in ), + .valid_o ( host_req_valid_o ), + .ready_i ( host_req_ready_i ), + .data_o ( spill_ax_channel_out ) ); - `FFARN(merge_r_q,merge_r_d,1'b0,clk_i,rst_ni) - `FFARN(s_rx_data_lower_q,s_rx_data_lower_d,'0,clk_i,rst_ni) + assign rr_out_req_ax = spill_ax_channel_out.ax_data; + assign rr_out_req_write = spill_ax_channel_out.write; - // ========================================================= - // W channel: Buffer. Cuts path and upsamples when needed - // ========================================================= + assign host_req_o.write = rr_out_req_write; + assign host_req_o.addr = rr_out_req_ax.addr; + assign host_req_o.beats = hyperbus_pkg::hyper_blen_t'(rr_out_req_ax.len) + + hyperbus_pkg::hyper_blen_t'(1); + assign host_req_o.size = rr_out_req_ax.size; + assign host_req_o.burst = (rr_out_req_ax.burst == axi_pkg::BURST_FIXED) ? + hyperbus_pkg::HyperBurstFixed : + hyperbus_pkg::HyperBurstIncr; + always_comb begin : proc_atomic_decode + host_req_o.atomic_op = (rr_out_req_ax.atop == '0) ? + hyperbus_pkg::HyperAtomicNone : + hyperbus_pkg::HyperAtomicInvalid; + unique case (rr_out_req_ax.atop) + axi_pkg::ATOP_ATOMICSWAP: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicSwap; + axi_pkg::ATOP_ATOMICCMP: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicCompare; + default: begin + if ((rr_out_req_ax.atop[5:4] == axi_pkg::ATOP_ATOMICSTORE) || + (rr_out_req_ax.atop[5:4] == axi_pkg::ATOP_ATOMICLOAD)) begin + unique case (rr_out_req_ax.atop[2:0]) + axi_pkg::ATOP_ADD: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicAdd; + axi_pkg::ATOP_CLR: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicClear; + axi_pkg::ATOP_EOR: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicXor; + axi_pkg::ATOP_SET: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicSet; + axi_pkg::ATOP_SMAX: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicSignedMax; + axi_pkg::ATOP_SMIN: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicSignedMin; + axi_pkg::ATOP_UMAX: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicUnsignedMax; + axi_pkg::ATOP_UMIN: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicUnsignedMin; + default:; + endcase + // AXI defines bit 3 as endianness for arithmetic atomics. + if (rr_out_req_ax.atop[3] && + ((rr_out_req_ax.atop[2:0] == axi_pkg::ATOP_ADD) || + (rr_out_req_ax.atop[2:0] >= axi_pkg::ATOP_SMAX))) begin + host_req_o.atomic_op = hyperbus_pkg::HyperAtomicInvalid; + end + end + end + endcase + host_req_o.atomic_return = rr_out_req_ax.atop[axi_pkg::ATOP_R_RESP]; + host_req_o.ordered = rr_out_req_ax.atop != '0; + end assign w_data_fifo_in.data = ser_out_req.w.data; assign w_data_fifo_in.strb = ser_out_req.w.strb; @@ -421,8 +320,8 @@ module hyperbus_axi #( stream_fifo #( .FALL_THROUGH ( 1'b0 ), .T ( axi_w_chan_t ), - .DEPTH ( 16 ) - ) wchan_stream_fifo ( + .DEPTH ( 16 ) + ) i_wchan_stream_fifo ( .clk_i, .rst_ni, .flush_i ( 1'b0 ), @@ -434,120 +333,60 @@ module hyperbus_axi #( .data_o ( w_data_fifo ), .valid_o ( w_data_valid ), .ready_i ( w_data_ready ) - ); - - hyperbus_w2phy #( - .AxiDataWidth ( AxiDataWidth ), - .BurstLength ( hyperbus_pkg::HyperBurstWidth ), - .T ( axi_w_chan_t ), - .NumPhys ( NumPhys ) - ) i_hyperbus_w2phy ( - .clk_i, - .rst_ni, - .size ( rr_out_req_ax.size ), - .len ( ax_blen_postinc ), - .is_a_write ( rr_out_req_write ), - .trans_handshake ( trans_handshake ), - .start_addr ( rr_out_req_ax.addr[AxiBusAddrWidth-1:0] ), - .data_i ( w_data_fifo ), - .axi_valid_i ( w_data_valid ), - .axi_ready_o ( w_data_ready ), - .data_o ( s_tx_data ), - .last_o ( s_tx_last ), - .strb_o ( s_tx_strb ), - .phy_valid_o ( s_tx_valid ), - .phy_ready_i ( s_tx_ready ) ); - always_comb begin - tx_o.data = s_tx_data; - tx_o.strb = s_tx_strb; - tx_o.last = s_tx_last; - tx_valid_o = s_tx_valid; - s_tx_ready = tx_ready_i; - split_w_d = split_w_q; - if( (NumPhys==2) & (~frontend_cfg_i.phys_in_use) ) begin - if(s_tx_valid & tx_ready_i) begin - split_w_d = split_w_q+1; - end - tx_o.last = s_tx_last & split_w_q; - tx_valid_o = s_tx_valid; - s_tx_ready = tx_ready_i & split_w_q; - tx_o.data = { s_tx_data[PhyDataWidth/2-1:0] , s_tx_data[PhyDataWidth/2-1:0] }; - tx_o.strb = { s_tx_strb[NumPhys-1:0] , s_tx_strb[NumPhys-1:0] }; - if(split_w_q) begin - tx_o.data = { s_tx_data[PhyDataWidth-1:PhyDataWidth/2] , s_tx_data[PhyDataWidth-1:PhyDataWidth/2] }; - tx_o.strb = { s_tx_strb[NumPhys*2-1:NumPhys] , s_tx_strb[NumPhys*2-1:NumPhys] }; - end - end - end - - `FFARN(split_w_q,split_w_d,1'b0,clk_i,rst_ni) - - // ============================ - // B channel: passthrough - // ============================ - - assign ser_out_rsp.b.resp = b_error_i ? axi_pkg::RESP_SLVERR : axi_pkg::RESP_OKAY; - assign ser_out_rsp.b.user = '0; - assign ser_out_rsp.b.id = '0; - assign ser_out_rsp.b_valid = b_valid_i; - assign b_ready_o = ser_out_req.b_ready; - - // ============================ - // Transfer status - // ============================ - - assign trans_active_o = trans_active_q; - - assign trans_active_set = trans_handshake; - assign trans_active_reset = (ser_out_rsp.r_valid & ser_out_req.r_ready & ser_out_rsp.r.last) | - (b_valid_i & b_ready_o); - - // Allow W transfers iff currently engaged in write (AW already received) - assign trans_wready_set = trans_active_set & rr_out_req_write; - assign trans_wready_reset = (tx_valid_o & tx_ready_i & tx_o.last); - - // Set overrules reset as a transfer must start before it finishes - always_comb begin : proc_comb_trans_active - trans_active_d = trans_active_q; - trans_wready_d = trans_wready_q; - if (trans_active_reset) trans_active_d = 1'b0; - if (trans_active_set) trans_active_d = 1'b1; - if (trans_wready_reset) trans_wready_d = 1'b0; - if (trans_wready_set) trans_wready_d = 1'b1; - end + assign host_w_o.data = w_data_fifo.data; + assign host_w_o.strb = w_data_fifo.strb; + assign host_w_o.last = w_data_fifo.last; + assign host_w_valid_o = w_data_valid; + assign w_data_ready = host_w_ready_i; + + assign ser_out_rsp.r.data = host_r_i.data; + assign ser_out_rsp.r.last = host_r_i.last; + always_comb begin : proc_host_resp + ser_out_rsp.r.resp = axi_pkg::RESP_OKAY; + ser_out_rsp.b.resp = axi_pkg::RESP_OKAY; + + if (host_r_i.resp == hyperbus_pkg::HyperRespDecodeError) begin + ser_out_rsp.r.resp = axi_pkg::RESP_DECERR; + end else if (host_r_i.resp != hyperbus_pkg::HyperRespOkay) begin + ser_out_rsp.r.resp = axi_pkg::RESP_SLVERR; + end - // ========================= - // Registers - // ========================= - - always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff - if(~rst_ni) begin - trans_active_q <= '0; - trans_wready_q <= '0; - end else begin - trans_active_q <= trans_active_d; - trans_wready_q <= trans_wready_d; + if (host_wrsp_i.resp == hyperbus_pkg::HyperRespDecodeError) begin + ser_out_rsp.b.resp = axi_pkg::RESP_DECERR; + end else if (host_wrsp_i.resp != hyperbus_pkg::HyperRespOkay) begin + ser_out_rsp.b.resp = axi_pkg::RESP_SLVERR; end end - // ========================= - // Assertions - // ========================= + assign ser_out_rsp.r.id = '0; + assign ser_out_rsp.r.user = '0; + assign ser_out_rsp.r_valid = host_r_valid_i; + assign host_r_ready_o = ser_out_req.r_ready; + + assign ser_out_rsp.b.user = '0; + assign ser_out_rsp.b.id = '0; + assign ser_out_rsp.b_valid = host_wrsp_valid_i; + assign host_wrsp_ready_o = ser_out_req.b_ready; // pragma translate_off `ifndef VERILATOR - initial assert (AxiDataWidth >= 16 && AxiDataWidth <= 1024) - else $error("AxiDatawidth must be a power of two within [16, 1024]."); - -// access_16b_align : assert property( -// @(posedge clk_i) trans_handshake & (rr_out_req_ax.size != '0) |-> (rr_out_req_ax.addr[0] == 1'b0)) -// else $fatal (1, "The address of a non-byte-size access must be 2-byte aligned."); - - burst_type : assert property( - @(posedge clk_i) trans_handshake |-> ( (rr_out_req_ax.burst == axi_pkg::BURST_INCR) || ((rr_out_req_ax.burst == axi_pkg::BURST_FIXED) && (rr_out_req_ax.len == '0)) ) ) - else $fatal (1, "Non-incremental burst passed; this is currently not supported."); + axi_burst_type : assert property( + @(posedge clk_i) host_req_valid_o && host_req_ready_i |-> + ((rr_out_req_ax.burst == axi_pkg::BURST_INCR) || + ((rr_out_req_ax.burst == axi_pkg::BURST_FIXED) && (rr_out_req_ax.len == '0)))) + else $fatal(1, "Only incremental bursts and single-beat fixed bursts are supported."); + + read_response_pending : assert property( + @(posedge clk_i) disable iff (!rst_ni) + axi_r_last_fire |-> (read_pending_q != '0)) + else $fatal(1, "AXI read response completed without a pending request."); + + write_response_pending : assert property( + @(posedge clk_i) disable iff (!rst_ni) + axi_b_fire |-> (write_pending_q != '0)) + else $fatal(1, "AXI write response completed without a pending request."); `endif // pragma translate_on diff --git a/src/hyperbus_backend.sv b/src/hyperbus_backend.sv index 899ff61..341d040 100644 --- a/src/hyperbus_backend.sv +++ b/src/hyperbus_backend.sv @@ -2,6 +2,9 @@ // Solderpad Hardware License, Version 0.51, see LICENSE for details. // SPDX-License-Identifier: SHL-0.51 +`include "common_cells/assertions.svh" +`include "common_cells/registers.svh" + module hyperbus_backend #( parameter int unsigned NumChips = -1, parameter int unsigned NumPhys = 2, @@ -9,15 +12,14 @@ module hyperbus_backend #( parameter int unsigned SyncStages = 2, parameter type hyper_rx_t = logic, parameter type hyper_tx_t = logic, - parameter type tf_cdc_t = logic, - parameter hyperbus_pkg::hyper_cfg_t RstCfg = '0 + parameter type hyper_cmd_t = logic ) ( input logic clk_i, input logic clk_90_i, input logic rst_ni, input logic test_mode_i, - input hyperbus_pkg::hyper_cfg_t cfg_apply_i, + input hyperbus_pkg::phy_cfg_t cfg_apply_i, input logic cfg_apply_valid_i, output logic cfg_apply_ready_o, @@ -30,12 +32,12 @@ module hyperbus_backend #( input hyper_tx_t tx_i, input logic tx_valid_i, output logic tx_ready_o, - output logic b_error_o, - output logic b_valid_o, - input logic b_ready_i, - input tf_cdc_t trans_i, - input logic trans_valid_i, - output logic trans_ready_o, + output logic wrsp_error_o, + output logic wrsp_valid_o, + input logic wrsp_ready_i, + input hyper_cmd_t cmd_i, + input logic cmd_valid_i, + output logic cmd_ready_o, output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, output logic [NumPhys-1:0] hyper_ck_o, @@ -49,20 +51,21 @@ module hyperbus_backend #( output logic [NumPhys-1:0] hyper_reset_no ); - hyperbus_pkg::hyper_cfg_t cfg_q, cfg_d; + hyperbus_pkg::phy_cfg_t cfg_q, cfg_d; logic phy_busy; logic cfg_apply_fire; + `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= 8) + `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) + `ASSERT_INIT(SyncStagesValid, SyncStages >= 2) + assign cfg_apply_ready_o = ~phy_busy; assign cfg_apply_fire = cfg_apply_valid_i & cfg_apply_ready_o; assign cfg_d = cfg_apply_fire ? cfg_apply_i : cfg_q; assign busy_o = phy_busy | cfg_apply_valid_i; assign tx_clk_delay_o = cfg_q.t_tx_clk_delay; - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) cfg_q <= RstCfg; - else cfg_q <= cfg_d; - end + `FFARN(cfg_q, cfg_d, '0, clk_i, rst_ni) hyperbus_phy_if #( .NumChips ( NumChips ), @@ -84,13 +87,13 @@ module hyperbus_backend #( .tx_i ( tx_i ), .tx_valid_i ( tx_valid_i ), .tx_ready_o ( tx_ready_o ), - .b_error_o ( b_error_o ), - .b_valid_o ( b_valid_o ), - .b_ready_i ( b_ready_i ), - .trans_i ( trans_i.trans ), - .trans_cs_i ( trans_i.cs ), - .trans_valid_i ( trans_valid_i ), - .trans_ready_o ( trans_ready_o ), + .b_error_o ( wrsp_error_o ), + .b_valid_o ( wrsp_valid_o ), + .b_ready_i ( wrsp_ready_i ), + .trans_i ( cmd_i.trans ), + .trans_cs_i ( cmd_i.cs ), + .trans_valid_i ( cmd_valid_i ), + .trans_ready_o ( cmd_ready_o ), .hyper_cs_no ( hyper_cs_no ), .hyper_ck_o ( hyper_ck_o ), .hyper_ck_no ( hyper_ck_no ), diff --git a/src/hyperbus_cfg_frontend.sv b/src/hyperbus_cfg_frontend.sv new file mode 100644 index 0000000..4ca88ea --- /dev/null +++ b/src/hyperbus_cfg_frontend.sv @@ -0,0 +1,162 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +`include "common_cells/registers.svh" +`include "common_cells/assertions.svh" + +module hyperbus_cfg_frontend #( + parameter int unsigned NumChips = -1, + parameter int unsigned NumPhys = 2, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type host_rule_t = logic, + parameter int unsigned RegDataWidth = -1 +) ( + input logic clk_i, + input logic rst_ni, + + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + + output logic drain_o, + input logic host_idle_i, + input logic trans_active_i, + + output hyperbus_pkg::phy_cfg_t cfg_apply_o, + output logic cfg_apply_valid_o, + input logic cfg_apply_ready_i, + input logic cfg_apply_done_i, + + output hyperbus_pkg::frontend_cfg_t frontend_cfg_o, + output host_rule_t [NumChips-1:0] chip_rules_o, + input logic decode_error_i +); + + typedef enum logic [2:0] { + CfgIdle, + CfgDrain, + CfgCommit, + CfgObserve, + CfgSend, + CfgWaitAck + } cfg_state_e; + + cfg_state_e cfg_state_d; + cfg_state_e cfg_state_q; + hyperbus_pkg::phy_cfg_t phy_cfg; + hyperbus_pkg::phy_cfg_t cfg_applied_d; + hyperbus_pkg::phy_cfg_t cfg_applied_q; + logic cfg_changed; + logic cfg_write_pending_d; + logic cfg_write_pending_q; + + `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= 8) + `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) + `ASSERT_INIT(RegDataWidthValid, RegDataWidth == 32) + + reg_req_t cfg_reg_req; + reg_rsp_t cfg_reg_rsp; + + assign cfg_changed = phy_cfg != cfg_applied_q; + assign drain_o = (cfg_state_q != CfgIdle) || cfg_changed; + assign cfg_apply_valid_o = cfg_state_q == CfgSend; + assign cfg_apply_o = phy_cfg; + + always_comb begin : proc_cfg_reg_gate + cfg_reg_req = reg_req_i; + reg_rsp_o = cfg_reg_rsp; + + if (reg_req_i.write) begin + cfg_reg_req.valid = reg_req_i.valid && (cfg_state_q == CfgCommit); + if (cfg_state_q != CfgCommit) begin + reg_rsp_o.ready = 1'b0; + reg_rsp_o.error = 1'b0; + reg_rsp_o.rdata = '0; + end + end else if (cfg_state_q != CfgIdle) begin + cfg_reg_req.valid = 1'b0; + reg_rsp_o.ready = 1'b0; + reg_rsp_o.error = 1'b0; + reg_rsp_o.rdata = '0; + end + end + + always_comb begin : proc_cfg_apply + cfg_state_d = cfg_state_q; + cfg_applied_d = cfg_applied_q; + cfg_write_pending_d = cfg_write_pending_q; + + unique case (cfg_state_q) + CfgIdle: begin + if (cfg_changed) begin + cfg_write_pending_d = 1'b0; + cfg_state_d = CfgDrain; + end else if (reg_req_i.valid && reg_req_i.write) begin + cfg_write_pending_d = 1'b1; + cfg_state_d = CfgDrain; + end + end + CfgDrain: begin + if (host_idle_i && !trans_active_i) begin + cfg_state_d = cfg_write_pending_q ? CfgCommit : CfgSend; + end + end + CfgCommit: begin + if (cfg_reg_rsp.ready) begin + cfg_write_pending_d = 1'b0; + cfg_state_d = CfgObserve; + end + end + CfgObserve: begin + if (cfg_changed) begin + cfg_state_d = CfgSend; + end else begin + cfg_state_d = CfgIdle; + end + end + CfgSend: begin + if (cfg_apply_ready_i) begin + if (cfg_apply_done_i) begin + cfg_applied_d = phy_cfg; + cfg_state_d = CfgIdle; + end else begin + cfg_state_d = CfgWaitAck; + end + end + end + CfgWaitAck: begin + if (cfg_apply_done_i) begin + cfg_applied_d = phy_cfg; + cfg_state_d = CfgIdle; + end + end + default: begin + cfg_state_d = CfgIdle; + end + endcase + end + + `FFARN(cfg_state_q, cfg_state_d, CfgIdle, clk_i, rst_ni) + `FFARN(cfg_applied_q, cfg_applied_d, '0, clk_i, rst_ni) + `FFARN(cfg_write_pending_q, cfg_write_pending_d, 1'b0, clk_i, rst_ni) + + hyperbus_cfg_regs #( + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .RegDataWidth ( RegDataWidth ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .addr_rule_t ( host_rule_t ) + ) i_cfg_regs ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .reg_req_i ( cfg_reg_req ), + .reg_rsp_o ( cfg_reg_rsp ), + .frontend_cfg_o ( frontend_cfg_o ), + .phy_cfg_o ( phy_cfg ), + .chip_rules_o ( chip_rules_o ), + .decode_error_i ( decode_error_i ) + ); + +endmodule diff --git a/src/hyperbus_cfg_regs.sv b/src/hyperbus_cfg_regs.sv index 6c6ebd1..18831a0 100644 --- a/src/hyperbus_cfg_regs.sv +++ b/src/hyperbus_cfg_regs.sv @@ -12,7 +12,7 @@ module hyperbus_cfg_regs #( parameter int unsigned RegDataWidth = -1, parameter type reg_req_t = logic, parameter type reg_rsp_t = logic, - parameter type rule_t = logic + parameter type addr_rule_t = logic ) ( input logic clk_i, input logic rst_ni, @@ -22,11 +22,9 @@ module hyperbus_cfg_regs #( output hyperbus_pkg::frontend_cfg_t frontend_cfg_o, output hyperbus_pkg::phy_cfg_t phy_cfg_o, - output rule_t [NumChips-1:0] chip_rules_o, - input trans_active_i + output addr_rule_t [NumChips-1:0] chip_rules_o, + input decode_error_i ); - `include "common_cells/registers.svh" - localparam int unsigned NumChipsMax = 8; localparam int unsigned NumRegs = 30; localparam int unsigned RegsBits = cf_math_pkg::idx_width(NumRegs); @@ -71,46 +69,33 @@ module hyperbus_cfg_regs #( logic pslverr; } cfg_apb_rsp_t; + hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__in_t cfg_hwif_in; hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t cfg_hwif_out; - rule_t [NumChipsMax-1:0] chip_rules_all; + addr_rule_t [NumChipsMax-1:0] chip_rules_all; reg_idx_t sel_reg; logic sel_reg_mapped; - logic cfg_access_active_d, cfg_access_active_q; - logic cfg_access_open; cfg_reg_req_t cfg_reg_req; cfg_reg_rsp_t cfg_reg_rsp; cfg_apb_req_t cfg_apb_req; cfg_apb_rsp_t cfg_apb_rsp; + assign cfg_hwif_in.status.decode_error.hwset = decode_error_i; + assign sel_reg = reg_req_i.addr[$clog2(RegStrbWidth) +: RegsBits]; assign sel_reg_mapped = (sel_reg < NumRegs); - assign cfg_access_open = ~trans_active_i | cfg_access_active_q; - assign reg_rsp_o.ready = cfg_access_open & - (~reg_req_i.valid | ~sel_reg_mapped | cfg_reg_rsp.ready); + assign reg_rsp_o.ready = !reg_req_i.valid || !sel_reg_mapped || cfg_reg_rsp.ready; assign reg_rsp_o.error = ~sel_reg_mapped | cfg_reg_rsp.error; assign reg_rsp_o.rdata = sel_reg_mapped ? RegDataWidth'(cfg_reg_rsp.rdata) : '0; - assign cfg_reg_req.valid = reg_req_i.valid & sel_reg_mapped & cfg_access_open; + assign cfg_reg_req.valid = reg_req_i.valid && sel_reg_mapped; assign cfg_reg_req.addr = {sel_reg, 2'b00}; assign cfg_reg_req.write = reg_req_i.write; assign cfg_reg_req.wdata = 32'(reg_req_i.wdata); assign cfg_reg_req.wstrb = cfg_strb_t'(reg_req_i.wstrb); - always_comb begin : proc_cfg_access - cfg_access_active_d = cfg_access_active_q; - if (!cfg_access_active_q && cfg_reg_req.valid) begin - cfg_access_active_d = 1'b1; - end - if (cfg_access_active_q && cfg_reg_rsp.ready) begin - cfg_access_active_d = 1'b0; - end - end - - `FFARN(cfg_access_active_q, cfg_access_active_d, 1'b0, clk_i, rst_ni); - always_comb begin : proc_chip_rules chip_rules_all = '0; for (int unsigned i = 0; i < NumChipsMax; i++) begin @@ -196,6 +181,7 @@ module hyperbus_cfg_regs #( .s_apb_pready ( cfg_apb_rsp.pready ), .s_apb_prdata ( cfg_apb_rsp.prdata ), .s_apb_pslverr ( cfg_apb_rsp.pslverr ), + .hwif_in ( cfg_hwif_in ), .hwif_out ( cfg_hwif_out ) ); @@ -205,10 +191,7 @@ module hyperbus_cfg_regs #( frontend_cfg_o.address_mask_msb = cfg_hwif_out.address_mask_msb.value.value; frontend_cfg_o.address_space = cfg_hwif_out.address_space.value.value; - frontend_cfg_o.phys_in_use = (NumPhys == 2) && - cfg_hwif_out.phys_in_use.value.value; - frontend_cfg_o.which_phy = (NumPhys == 2) && - cfg_hwif_out.which_phy.value.value; + frontend_cfg_o.dual_phy = (NumPhys == 2) && cfg_hwif_out.dual_phy.value.value; phy_cfg_o.chip.t_latency_access = cfg_hwif_out.t_latency_access.value.value; phy_cfg_o.chip.en_latency_additional = cfg_hwif_out.en_latency_additional.value.value; @@ -224,10 +207,7 @@ module hyperbus_cfg_regs #( cfg_hwif_out.t_tx_clk_delay.coarse.value, cfg_hwif_out.t_tx_clk_delay.fine.value }; - phy_cfg_o.phys_in_use = (NumPhys == 2) && - cfg_hwif_out.phys_in_use.value.value; - phy_cfg_o.which_phy = (NumPhys == 2) && - cfg_hwif_out.which_phy.value.value; + phy_cfg_o.dual_phy = (NumPhys == 2) && cfg_hwif_out.dual_phy.value.value; end for (genvar i = 0; unsigned'(i) < NumChipsMax; i++) begin : gen_chip_rules diff --git a/src/hyperbus_frontend.sv b/src/hyperbus_frontend.sv deleted file mode 100644 index 7d08b99..0000000 --- a/src/hyperbus_frontend.sv +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2023 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 - -module hyperbus_frontend #( - parameter int unsigned NumChips = -1, - parameter int unsigned NumPhys = 2, - parameter int unsigned AxiAddrWidth = -1, - parameter int unsigned AxiDataWidth = -1, - parameter int unsigned AxiIdWidth = -1, - parameter int unsigned AxiUserWidth = -1, - parameter type axi_req_t = logic, - parameter type axi_rsp_t = logic, - parameter type reg_req_t = logic, - parameter type reg_rsp_t = logic, - parameter type axi_rule_t = logic, - parameter type hyper_rx_t = logic, - parameter type hyper_tx_t = logic, - parameter type tf_cdc_t = logic, - parameter int unsigned RegAddrWidth = -1, - parameter int unsigned RegDataWidth = -1, - parameter logic [RegDataWidth-1:0] RstChipBase = 'h0, - parameter logic [RegDataWidth-1:0] RstChipSpace = 'h1_0000, - parameter hyperbus_pkg::hyper_cfg_t RstCfg = '0 -) ( - input logic clk_i, - input logic rst_ni, - - input axi_req_t axi_req_i, - output axi_rsp_t axi_rsp_o, - - input reg_req_t reg_req_i, - output reg_rsp_t reg_rsp_o, - - output hyperbus_pkg::hyper_cfg_t cfg_o, - output hyperbus_pkg::hyper_cfg_t cfg_apply_o, - output logic cfg_apply_valid_o, - input logic cfg_apply_ready_i, - - input hyper_rx_t rx_i, - input logic rx_valid_i, - output logic rx_ready_o, - output hyper_tx_t tx_o, - output logic tx_valid_o, - input logic tx_ready_i, - input logic b_error_i, - input logic b_valid_i, - output logic b_ready_o, - output tf_cdc_t trans_o, - output logic trans_valid_o, - input logic trans_ready_i -); - - typedef enum logic [1:0] { - CfgIdle, - CfgSend, - CfgWaitAck - } cfg_state_e; - - axi_rule_t [NumChips-1:0] chip_rules; - logic trans_active; - logic axi_trans_valid; - logic axi_trans_ready; - cfg_state_e cfg_state_d, cfg_state_q; - hyperbus_pkg::hyper_cfg_t cfg_applied_d, cfg_applied_q; - hyperbus_pkg::hyper_cfg_t cfg_pending_d, cfg_pending_q; - logic cfg_changed; - logic cfg_apply_busy; - - assign cfg_changed = cfg_o != cfg_applied_q; - assign cfg_apply_busy = (cfg_state_q != CfgIdle) | cfg_changed; - assign cfg_apply_valid_o = cfg_state_q == CfgSend; - assign cfg_apply_o = cfg_pending_q; - - always_comb begin - cfg_state_d = cfg_state_q; - cfg_applied_d = cfg_applied_q; - cfg_pending_d = cfg_pending_q; - - unique case (cfg_state_q) - CfgIdle: begin - if (cfg_changed) begin - cfg_pending_d = cfg_o; - cfg_state_d = CfgSend; - end - end - CfgSend: begin - if (cfg_apply_ready_i) begin - cfg_state_d = CfgWaitAck; - end - end - CfgWaitAck: begin - if (cfg_apply_ready_i) begin - cfg_applied_d = cfg_pending_q; - cfg_state_d = CfgIdle; - end - end - default: cfg_state_d = CfgIdle; - endcase - end - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - cfg_state_q <= CfgIdle; - cfg_applied_q <= RstCfg; - cfg_pending_q <= RstCfg; - end else begin - cfg_state_q <= cfg_state_d; - cfg_applied_q <= cfg_applied_d; - cfg_pending_q <= cfg_pending_d; - end - end - - hyperbus_cfg_regs #( - .NumChips ( NumChips ), - .NumPhys ( NumPhys ), - .RegAddrWidth ( RegAddrWidth ), - .RegDataWidth ( RegDataWidth ), - .reg_req_t ( reg_req_t ), - .reg_rsp_t ( reg_rsp_t ), - .rule_t ( axi_rule_t ), - .RstChipBase ( RstChipBase ), - .RstChipSpace ( RstChipSpace ), - .RstCfg ( RstCfg ) - ) i_cfg_regs ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .reg_req_i ( reg_req_i ), - .reg_rsp_o ( reg_rsp_o ), - .cfg_o ( cfg_o ), - .chip_rules_o ( chip_rules ), - .trans_active_i ( trans_active ) - ); - - hyperbus_axi #( - .AxiDataWidth ( AxiDataWidth ), - .AxiAddrWidth ( AxiAddrWidth ), - .AxiIdWidth ( AxiIdWidth ), - .AxiUserWidth ( AxiUserWidth ), - .axi_req_t ( axi_req_t ), - .axi_rsp_t ( axi_rsp_t ), - .NumChips ( NumChips ), - .NumPhys ( NumPhys ), - .hyper_rx_t ( hyper_rx_t ), - .hyper_tx_t ( hyper_tx_t ), - .rule_t ( axi_rule_t ) - ) i_axi_slave ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .axi_req_i ( axi_req_i ), - .axi_rsp_o ( axi_rsp_o ), - .rx_i ( rx_i ), - .rx_valid_i ( rx_valid_i ), - .rx_ready_o ( rx_ready_o ), - .tx_o ( tx_o ), - .tx_valid_o ( tx_valid_o ), - .tx_ready_i ( tx_ready_i ), - .b_error_i ( b_error_i ), - .b_valid_i ( b_valid_i ), - .b_ready_o ( b_ready_o ), - .trans_o ( trans_o.trans ), - .trans_cs_o ( trans_o.cs ), - .trans_valid_o ( axi_trans_valid ), - .trans_ready_i ( axi_trans_ready ), - .chip_rules_i ( chip_rules ), - .which_phy_i ( cfg_o.which_phy ), - .phys_in_use_i ( cfg_o.phys_in_use ), - .addr_mask_msb_i ( cfg_o.address_mask_msb ), - .addr_space_i ( cfg_o.address_space ), - .trans_active_o ( trans_active ) - ); - - assign trans_valid_o = axi_trans_valid & ~cfg_apply_busy; - assign axi_trans_ready = trans_ready_i & ~cfg_apply_busy; - -endmodule diff --git a/src/hyperbus_iso_bridge.sv b/src/hyperbus_iso_bridge.sv index fdb4577..d351b2f 100644 --- a/src/hyperbus_iso_bridge.sv +++ b/src/hyperbus_iso_bridge.sv @@ -2,21 +2,24 @@ // Solderpad Hardware License, Version 0.51, see LICENSE for details. // SPDX-License-Identifier: SHL-0.51 +`include "common_cells/registers.svh" + module hyperbus_iso_bridge #( parameter int unsigned RxFifoLogDepth = 8, parameter int unsigned TxFifoLogDepth = 3, - parameter type hyper_rx_t = logic, - parameter type hyper_tx_t = logic, - parameter type tf_cdc_t = logic + parameter type hyper_rx_t = logic, + parameter type hyper_tx_t = logic, + parameter type hyper_cmd_t = logic ) ( input logic clk_sys_i, input logic rst_sys_ni, input logic clk_phy_i, input logic rst_phy_ni, - input hyperbus_pkg::hyper_cfg_t cfg_apply_i, + input hyperbus_pkg::phy_cfg_t cfg_apply_i, input logic frontend_cfg_apply_valid_i, output logic frontend_cfg_apply_ready_o, + output logic frontend_cfg_apply_done_o, output hyper_rx_t frontend_rx_o, output logic frontend_rx_valid_o, @@ -24,12 +27,12 @@ module hyperbus_iso_bridge #( input hyper_tx_t frontend_tx_i, input logic frontend_tx_valid_i, output logic frontend_tx_ready_o, - output logic frontend_b_error_o, - output logic frontend_b_valid_o, - input logic frontend_b_ready_i, - input tf_cdc_t frontend_trans_i, - input logic frontend_trans_valid_i, - output logic frontend_trans_ready_o, + output logic frontend_wrsp_error_o, + output logic frontend_wrsp_valid_o, + input logic frontend_wrsp_ready_i, + input hyper_cmd_t frontend_cmd_i, + input logic frontend_cmd_valid_i, + output logic frontend_cmd_ready_o, input hyper_rx_t backend_rx_i, input logic backend_rx_valid_i, @@ -37,71 +40,60 @@ module hyperbus_iso_bridge #( output hyper_tx_t backend_tx_o, output logic backend_tx_valid_o, input logic backend_tx_ready_i, - input logic backend_b_error_i, - input logic backend_b_valid_i, - output logic backend_b_ready_o, - output tf_cdc_t backend_trans_o, - output logic backend_trans_valid_o, - input logic backend_trans_ready_i, - - output hyperbus_pkg::hyper_cfg_t cfg_apply_o, + input logic backend_wrsp_error_i, + input logic backend_wrsp_valid_i, + output logic backend_wrsp_ready_o, + output hyper_cmd_t backend_cmd_o, + output logic backend_cmd_valid_o, + input logic backend_cmd_ready_i, + + output hyperbus_pkg::phy_cfg_t cfg_apply_o, output logic cfg_apply_valid_o, input logic cfg_apply_ready_i ); - hyperbus_pkg::hyper_cfg_t cfg_apply_data_q; + hyperbus_pkg::phy_cfg_t cfg_apply_data_q; logic cfg_apply_src_fire; - tf_cdc_t trans_data_q; - logic trans_src_fire; - logic b_error_q; - logic b_src_fire; + logic cfg_apply_pending_d; + logic cfg_apply_pending_q; + hyper_cmd_t cmd_data_q; + logic cmd_src_fire; + logic wrsp_error_q; + logic wrsp_src_fire; hyper_tx_t tx_fifo_data; logic tx_fifo_valid; logic tx_fifo_ready; - logic [TxFifoLogDepth-1:0] tx_fifo_usage; - hyper_tx_t tx_data_q; - logic tx_src_fire; hyper_rx_t rx_src_fifo_data; logic rx_src_fifo_valid; logic rx_src_fifo_ready; - logic [RxFifoLogDepth-1:0] rx_src_fifo_usage; - hyper_rx_t rx_data_q; logic rx_iso_valid; logic rx_iso_ready; - logic rx_src_fire; + hyper_rx_t rx_iso_data; hyper_rx_t rx_fifo_data; logic rx_fifo_valid; logic rx_fifo_ready; - logic [RxFifoLogDepth-1:0] rx_fifo_usage; - - assign cfg_apply_src_fire = frontend_cfg_apply_valid_i & frontend_cfg_apply_ready_o; - assign trans_src_fire = frontend_trans_valid_i & frontend_trans_ready_o; - assign tx_src_fire = tx_fifo_valid & tx_fifo_ready; - assign rx_src_fire = rx_src_fifo_valid & rx_src_fifo_ready; - assign b_src_fire = backend_b_valid_i & backend_b_ready_o; - - always_ff @(posedge clk_sys_i or negedge rst_sys_ni) begin - if (!rst_sys_ni) begin - cfg_apply_data_q <= '0; - trans_data_q <= '0; - tx_data_q <= '0; - end else begin - if (cfg_apply_src_fire) cfg_apply_data_q <= cfg_apply_i; - if (trans_src_fire) trans_data_q <= frontend_trans_i; - if (tx_src_fire) tx_data_q <= tx_fifo_data; - end - end - always_ff @(posedge clk_phy_i or negedge rst_phy_ni) begin - if (!rst_phy_ni) begin - b_error_q <= 1'b0; - rx_data_q <= '0; - end else begin - if (b_src_fire) b_error_q <= backend_b_error_i; - if (rx_src_fire) rx_data_q <= rx_src_fifo_data; + assign cfg_apply_src_fire = frontend_cfg_apply_valid_i && frontend_cfg_apply_ready_o; + assign frontend_cfg_apply_done_o = + cfg_apply_pending_q && frontend_cfg_apply_ready_o; + assign cmd_src_fire = frontend_cmd_valid_i && frontend_cmd_ready_o; + assign wrsp_src_fire = backend_wrsp_valid_i && backend_wrsp_ready_o; + + always_comb begin + cfg_apply_pending_d = cfg_apply_pending_q; + + if (cfg_apply_src_fire) begin + cfg_apply_pending_d = 1'b1; + end else if (frontend_cfg_apply_done_o) begin + cfg_apply_pending_d = 1'b0; end end + `FFLARN(cfg_apply_data_q, cfg_apply_i, cfg_apply_src_fire, '0, clk_sys_i, rst_sys_ni) + `FFARN(cfg_apply_pending_q, cfg_apply_pending_d, 1'b0, clk_sys_i, rst_sys_ni) + `FFLARN(cmd_data_q, frontend_cmd_i, cmd_src_fire, '0, clk_sys_i, rst_sys_ni) + `FFLARN(wrsp_error_q, backend_wrsp_error_i, wrsp_src_fire, 1'b0, clk_phy_i, rst_phy_ni) + isochronous_4phase_handshake i_iso_cfg_apply ( .src_clk_i ( clk_sys_i ), .src_rst_ni ( rst_sys_ni ), @@ -115,31 +107,31 @@ module hyperbus_iso_bridge #( assign cfg_apply_o = cfg_apply_data_q; - isochronous_4phase_handshake i_iso_trans ( + isochronous_4phase_handshake i_iso_cmd ( .src_clk_i ( clk_sys_i ), .src_rst_ni ( rst_sys_ni ), - .src_valid_i ( frontend_trans_valid_i ), - .src_ready_o ( frontend_trans_ready_o ), + .src_valid_i ( frontend_cmd_valid_i ), + .src_ready_o ( frontend_cmd_ready_o ), .dst_clk_i ( clk_phy_i ), .dst_rst_ni ( rst_phy_ni ), - .dst_valid_o ( backend_trans_valid_o ), - .dst_ready_i ( backend_trans_ready_i ) + .dst_valid_o ( backend_cmd_valid_o ), + .dst_ready_i ( backend_cmd_ready_i ) ); - assign backend_trans_o = trans_data_q; + assign backend_cmd_o = cmd_data_q; - isochronous_4phase_handshake i_iso_b ( + isochronous_4phase_handshake i_iso_wrsp ( .src_clk_i ( clk_phy_i ), .src_rst_ni ( rst_phy_ni ), - .src_valid_i ( backend_b_valid_i ), - .src_ready_o ( backend_b_ready_o ), + .src_valid_i ( backend_wrsp_valid_i ), + .src_ready_o ( backend_wrsp_ready_o ), .dst_clk_i ( clk_sys_i ), .dst_rst_ni ( rst_sys_ni ), - .dst_valid_o ( frontend_b_valid_o ), - .dst_ready_i ( frontend_b_ready_i ) + .dst_valid_o ( frontend_wrsp_valid_o ), + .dst_ready_i ( frontend_wrsp_ready_i ) ); - assign frontend_b_error_o = b_error_q; + assign frontend_wrsp_error_o = wrsp_error_q; stream_fifo #( .FALL_THROUGH ( 1'b0 ), @@ -150,7 +142,7 @@ module hyperbus_iso_bridge #( .rst_ni ( rst_sys_ni ), .flush_i ( 1'b0 ), .testmode_i ( 1'b0 ), - .usage_o ( tx_fifo_usage ), + .usage_o ( ), .data_i ( frontend_tx_i ), .valid_i ( frontend_tx_valid_i ), .ready_o ( frontend_tx_ready_o ), @@ -159,19 +151,21 @@ module hyperbus_iso_bridge #( .ready_i ( tx_fifo_ready ) ); - isochronous_4phase_handshake i_iso_tx ( + isochronous_spill_register #( + .T ( hyper_tx_t ) + ) i_iso_tx ( .src_clk_i ( clk_sys_i ), .src_rst_ni ( rst_sys_ni ), .src_valid_i ( tx_fifo_valid ), .src_ready_o ( tx_fifo_ready ), + .src_data_i ( tx_fifo_data ), .dst_clk_i ( clk_phy_i ), .dst_rst_ni ( rst_phy_ni ), .dst_valid_o ( backend_tx_valid_o ), - .dst_ready_i ( backend_tx_ready_i ) + .dst_ready_i ( backend_tx_ready_i ), + .dst_data_o ( backend_tx_o ) ); - assign backend_tx_o = tx_data_q; - stream_fifo #( .FALL_THROUGH ( 1'b0 ), .DEPTH ( 1 << RxFifoLogDepth ), @@ -181,7 +175,7 @@ module hyperbus_iso_bridge #( .rst_ni ( rst_phy_ni ), .flush_i ( 1'b0 ), .testmode_i ( 1'b0 ), - .usage_o ( rx_src_fifo_usage ), + .usage_o ( ), .data_i ( backend_rx_i ), .valid_i ( backend_rx_valid_i ), .ready_o ( backend_rx_ready_o ), @@ -190,15 +184,19 @@ module hyperbus_iso_bridge #( .ready_i ( rx_src_fifo_ready ) ); - isochronous_4phase_handshake i_iso_rx ( + isochronous_spill_register #( + .T ( hyper_rx_t ) + ) i_iso_rx ( .src_clk_i ( clk_phy_i ), .src_rst_ni ( rst_phy_ni ), .src_valid_i ( rx_src_fifo_valid ), .src_ready_o ( rx_src_fifo_ready ), + .src_data_i ( rx_src_fifo_data ), .dst_clk_i ( clk_sys_i ), .dst_rst_ni ( rst_sys_ni ), .dst_valid_o ( rx_iso_valid ), - .dst_ready_i ( rx_iso_ready ) + .dst_ready_i ( rx_iso_ready ), + .dst_data_o ( rx_iso_data ) ); stream_fifo #( @@ -210,8 +208,8 @@ module hyperbus_iso_bridge #( .rst_ni ( rst_sys_ni ), .flush_i ( 1'b0 ), .testmode_i ( 1'b0 ), - .usage_o ( rx_fifo_usage ), - .data_i ( rx_data_q ), + .usage_o ( ), + .data_i ( rx_iso_data ), .valid_i ( rx_iso_valid ), .ready_o ( rx_iso_ready ), .data_o ( rx_fifo_data ), diff --git a/src/hyperbus_isochronous.sv b/src/hyperbus_isochronous.sv index 6abd493..11b0f05 100644 --- a/src/hyperbus_isochronous.sv +++ b/src/hyperbus_isochronous.sv @@ -2,6 +2,9 @@ // Solderpad Hardware License, Version 0.51, see LICENSE for details. // SPDX-License-Identifier: SHL-0.51 +`include "hyperbus/typedef.svh" +`include "common_cells/assertions.svh" + module hyperbus_isochronous #( parameter int unsigned NumChips = -1, parameter int unsigned NumPhys = 2, @@ -11,22 +14,12 @@ module hyperbus_isochronous #( parameter int unsigned AxiUserWidth = -1, parameter type axi_req_t = logic, parameter type axi_rsp_t = logic, - parameter type axi_w_chan_t = logic, - parameter type axi_b_chan_t = logic, - parameter type axi_ar_chan_t = logic, - parameter type axi_r_chan_t = logic, - parameter type axi_aw_chan_t = logic, - parameter int unsigned RegAddrWidth = -1, parameter int unsigned RegDataWidth = -1, - parameter int unsigned MinFreqMHz = 100, parameter type reg_req_t = logic, parameter type reg_rsp_t = logic, parameter type axi_rule_t = logic, parameter int unsigned RxFifoLogDepth = 8, parameter int unsigned TxFifoLogDepth = 3, - parameter logic [RegDataWidth-1:0] RstChipBase = 'h0, - parameter logic [RegDataWidth-1:0] RstChipSpace = 'h1_0000, - parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys, MinFreqMHz), parameter int unsigned PhyStartupCycles = 300 * 200, parameter int unsigned SyncStages = 2 ) ( @@ -55,6 +48,8 @@ module hyperbus_isochronous #( output logic [NumPhys-1:0] hyper_reset_no ); + `ASSERT_INIT(AxiAddrWidthValid, AxiAddrWidth >= $clog2(AxiDataWidth / 8)) + typedef struct packed { logic [(16*NumPhys)-1:0] data; logic last; @@ -70,31 +65,57 @@ module hyperbus_isochronous #( typedef struct packed { hyperbus_pkg::hyper_tf_t trans; logic [NumChips-1:0] cs; - } tf_cdc_t; + } hyper_cmd_t; + + typedef logic [AxiAddrWidth-1:0] host_addr_t; + typedef logic [AxiDataWidth-1:0] host_data_t; + typedef logic [AxiDataWidth/8-1:0] host_strb_t; + `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) logic clk_backend; logic clk_backend_90; logic rst_backend_n; - hyperbus_pkg::hyper_cfg_t cfg_frontend_apply; - logic cfg_frontend_apply_valid; - logic cfg_frontend_apply_ready; - hyperbus_pkg::hyper_cfg_t cfg_backend_apply; - logic cfg_backend_apply_valid; - logic cfg_backend_apply_ready; + hyperbus_pkg::phy_cfg_t frontend_cfg_apply; + logic frontend_cfg_apply_valid; + logic frontend_cfg_apply_ready; + logic frontend_cfg_apply_done; + logic frontend_drain; + logic host_idle; + hyperbus_pkg::phy_cfg_t backend_cfg_apply; + logic backend_cfg_apply_valid; + logic backend_cfg_apply_ready; logic rst_backend_async_n; - hyper_rx_t frontend_rx; - logic frontend_rx_valid; - logic frontend_rx_ready; - hyper_tx_t frontend_tx; - logic frontend_tx_valid; - logic frontend_tx_ready; - logic frontend_b_error; - logic frontend_b_valid; - logic frontend_b_ready; - tf_cdc_t frontend_trans; - logic frontend_trans_valid; - logic frontend_trans_ready; + hyperbus_pkg::frontend_cfg_t frontend_cfg; + axi_rule_t [NumChips-1:0] frontend_chip_rules; + + host_req_t host_req; + logic host_req_valid; + logic host_req_ready; + host_w_t host_w; + logic host_w_valid; + logic host_w_ready; + host_r_t host_r; + logic host_r_valid; + logic host_r_ready; + host_wrsp_t host_wrsp; + logic host_wrsp_valid; + logic host_wrsp_ready; + + hyper_rx_t midend_rx; + logic midend_rx_valid; + logic midend_rx_ready; + hyper_tx_t midend_tx; + logic midend_tx_valid; + logic midend_tx_ready; + logic midend_wrsp_error; + logic midend_wrsp_valid; + logic midend_wrsp_ready; + hyper_cmd_t midend_cmd; + logic midend_cmd_valid; + logic midend_cmd_ready; + logic midend_trans_active; + logic midend_decode_error; hyper_rx_t backend_rx; logic backend_rx_valid; @@ -102,12 +123,12 @@ module hyperbus_isochronous #( hyper_tx_t backend_tx; logic backend_tx_valid; logic backend_tx_ready; - logic backend_b_error; - logic backend_b_valid; - logic backend_b_ready; - tf_cdc_t backend_trans; - logic backend_trans_valid; - logic backend_trans_ready; + logic backend_wrsp_error; + logic backend_wrsp_valid; + logic backend_wrsp_ready; + hyper_cmd_t backend_cmd; + logic backend_cmd_valid; + logic backend_cmd_ready; hyperbus_clk_gen i_clk_gen ( .clk_i ( clk_sys_i ), @@ -127,49 +148,106 @@ module hyperbus_isochronous #( .init_no ( ) ); - hyperbus_frontend #( + hyperbus_cfg_frontend #( .NumChips ( NumChips ), .NumPhys ( NumPhys ), - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidth ( AxiIdWidth ), - .AxiUserWidth ( AxiUserWidth ), - .axi_req_t ( axi_req_t ), - .axi_rsp_t ( axi_rsp_t ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), - .axi_rule_t ( axi_rule_t ), - .hyper_rx_t ( hyper_rx_t ), - .hyper_tx_t ( hyper_tx_t ), - .tf_cdc_t ( tf_cdc_t ), - .RegAddrWidth ( RegAddrWidth ), - .RegDataWidth ( RegDataWidth ), - .RstChipBase ( RstChipBase ), - .RstChipSpace ( RstChipSpace ), - .RstCfg ( RstCfg ) - ) i_frontend ( + .host_rule_t ( axi_rule_t ), + .RegDataWidth ( RegDataWidth ) + ) i_cfg_frontend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), - .axi_req_i ( axi_req_i ), - .axi_rsp_o ( axi_rsp_o ), .reg_req_i ( reg_req_i ), .reg_rsp_o ( reg_rsp_o ), - .cfg_o ( ), - .cfg_apply_o ( cfg_frontend_apply ), - .cfg_apply_valid_o ( cfg_frontend_apply_valid ), - .cfg_apply_ready_i ( cfg_frontend_apply_ready ), - .rx_i ( frontend_rx ), - .rx_valid_i ( frontend_rx_valid ), - .rx_ready_o ( frontend_rx_ready ), - .tx_o ( frontend_tx ), - .tx_valid_o ( frontend_tx_valid ), - .tx_ready_i ( frontend_tx_ready ), - .b_error_i ( frontend_b_error ), - .b_valid_i ( frontend_b_valid ), - .b_ready_o ( frontend_b_ready ), - .trans_o ( frontend_trans ), - .trans_valid_o ( frontend_trans_valid ), - .trans_ready_i ( frontend_trans_ready ) + .drain_o ( frontend_drain ), + .host_idle_i ( host_idle ), + .trans_active_i ( midend_trans_active ), + .cfg_apply_o ( frontend_cfg_apply ), + .cfg_apply_valid_o ( frontend_cfg_apply_valid ), + .cfg_apply_ready_i ( frontend_cfg_apply_ready ), + .cfg_apply_done_i ( frontend_cfg_apply_done ), + .frontend_cfg_o ( frontend_cfg ), + .chip_rules_o ( frontend_chip_rules ), + .decode_error_i ( midend_decode_error ) + ); + + hyperbus_axi_frontend #( + .AxiDataWidth ( AxiDataWidth ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .host_req_t ( host_req_t ), + .host_w_t ( host_w_t ), + .host_r_t ( host_r_t ), + .host_wrsp_t ( host_wrsp_t ) + ) i_axi_frontend ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .drain_i ( frontend_drain ), + .idle_o ( host_idle ), + .axi_req_i ( axi_req_i ), + .axi_rsp_o ( axi_rsp_o ), + .host_req_o ( host_req ), + .host_req_valid_o ( host_req_valid ), + .host_req_ready_i ( host_req_ready ), + .host_w_o ( host_w ), + .host_w_valid_o ( host_w_valid ), + .host_w_ready_i ( host_w_ready ), + .host_r_i ( host_r ), + .host_r_valid_i ( host_r_valid ), + .host_r_ready_o ( host_r_ready ), + .host_wrsp_i ( host_wrsp ), + .host_wrsp_valid_i ( host_wrsp_valid ), + .host_wrsp_ready_o ( host_wrsp_ready ) + ); + + hyperbus_midend #( + .HostAddrWidth ( AxiAddrWidth ), + .HostDataWidth ( AxiDataWidth ), + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .host_req_t ( host_req_t ), + .host_w_t ( host_w_t ), + .host_r_t ( host_r_t ), + .host_wrsp_t ( host_wrsp_t ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .hyper_cmd_t ( hyper_cmd_t ), + .rule_t ( axi_rule_t ) + ) i_midend ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .host_req_i ( host_req ), + .host_req_valid_i ( host_req_valid ), + .host_req_ready_o ( host_req_ready ), + .host_w_i ( host_w ), + .host_w_valid_i ( host_w_valid ), + .host_w_ready_o ( host_w_ready ), + .host_r_o ( host_r ), + .host_r_valid_o ( host_r_valid ), + .host_r_ready_i ( host_r_ready ), + .host_wrsp_o ( host_wrsp ), + .host_wrsp_valid_o ( host_wrsp_valid ), + .host_wrsp_ready_i ( host_wrsp_ready ), + .frontend_cfg_i ( frontend_cfg ), + .chip_rules_i ( frontend_chip_rules ), + .trans_active_o ( midend_trans_active ), + .decode_error_o ( midend_decode_error ), + .rx_i ( midend_rx ), + .rx_valid_i ( midend_rx_valid ), + .rx_ready_o ( midend_rx_ready ), + .tx_o ( midend_tx ), + .tx_valid_o ( midend_tx_valid ), + .tx_ready_i ( midend_tx_ready ), + .wrsp_error_i ( midend_wrsp_error ), + .wrsp_valid_i ( midend_wrsp_valid ), + .wrsp_ready_o ( midend_wrsp_ready ), + .cmd_o ( midend_cmd ), + .cmd_valid_o ( midend_cmd_valid ), + .cmd_ready_i ( midend_cmd_ready ) ); hyperbus_iso_bridge #( @@ -177,42 +255,43 @@ module hyperbus_isochronous #( .TxFifoLogDepth ( TxFifoLogDepth ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), - .tf_cdc_t ( tf_cdc_t ) + .hyper_cmd_t ( hyper_cmd_t ) ) i_bridge ( .clk_sys_i ( clk_sys_i ), .rst_sys_ni ( rst_sys_ni ), .clk_phy_i ( clk_backend ), .rst_phy_ni ( rst_backend_n ), - .cfg_apply_i ( cfg_frontend_apply ), - .frontend_cfg_apply_valid_i ( cfg_frontend_apply_valid ), - .frontend_cfg_apply_ready_o ( cfg_frontend_apply_ready ), - .frontend_rx_o ( frontend_rx ), - .frontend_rx_valid_o ( frontend_rx_valid ), - .frontend_rx_ready_i ( frontend_rx_ready ), - .frontend_tx_i ( frontend_tx ), - .frontend_tx_valid_i ( frontend_tx_valid ), - .frontend_tx_ready_o ( frontend_tx_ready ), - .frontend_b_error_o ( frontend_b_error ), - .frontend_b_valid_o ( frontend_b_valid ), - .frontend_b_ready_i ( frontend_b_ready ), - .frontend_trans_i ( frontend_trans ), - .frontend_trans_valid_i ( frontend_trans_valid ), - .frontend_trans_ready_o ( frontend_trans_ready ), + .cfg_apply_i ( frontend_cfg_apply ), + .frontend_cfg_apply_valid_i ( frontend_cfg_apply_valid ), + .frontend_cfg_apply_ready_o ( frontend_cfg_apply_ready ), + .frontend_cfg_apply_done_o ( frontend_cfg_apply_done ), + .frontend_rx_o ( midend_rx ), + .frontend_rx_valid_o ( midend_rx_valid ), + .frontend_rx_ready_i ( midend_rx_ready ), + .frontend_tx_i ( midend_tx ), + .frontend_tx_valid_i ( midend_tx_valid ), + .frontend_tx_ready_o ( midend_tx_ready ), + .frontend_wrsp_error_o ( midend_wrsp_error ), + .frontend_wrsp_valid_o ( midend_wrsp_valid ), + .frontend_wrsp_ready_i ( midend_wrsp_ready ), + .frontend_cmd_i ( midend_cmd ), + .frontend_cmd_valid_i ( midend_cmd_valid ), + .frontend_cmd_ready_o ( midend_cmd_ready ), .backend_rx_i ( backend_rx ), .backend_rx_valid_i ( backend_rx_valid ), .backend_rx_ready_o ( backend_rx_ready ), .backend_tx_o ( backend_tx ), .backend_tx_valid_o ( backend_tx_valid ), .backend_tx_ready_i ( backend_tx_ready ), - .backend_b_error_i ( backend_b_error ), - .backend_b_valid_i ( backend_b_valid ), - .backend_b_ready_o ( backend_b_ready ), - .backend_trans_o ( backend_trans ), - .backend_trans_valid_o ( backend_trans_valid ), - .backend_trans_ready_i ( backend_trans_ready ), - .cfg_apply_o ( cfg_backend_apply ), - .cfg_apply_valid_o ( cfg_backend_apply_valid ), - .cfg_apply_ready_i ( cfg_backend_apply_ready ) + .backend_wrsp_error_i ( backend_wrsp_error ), + .backend_wrsp_valid_i ( backend_wrsp_valid ), + .backend_wrsp_ready_o ( backend_wrsp_ready ), + .backend_cmd_o ( backend_cmd ), + .backend_cmd_valid_o ( backend_cmd_valid ), + .backend_cmd_ready_i ( backend_cmd_ready ), + .cfg_apply_o ( backend_cfg_apply ), + .cfg_apply_valid_o ( backend_cfg_apply_valid ), + .cfg_apply_ready_i ( backend_cfg_apply_ready ) ); hyperbus_backend #( @@ -222,16 +301,15 @@ module hyperbus_isochronous #( .SyncStages ( SyncStages ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), - .tf_cdc_t ( tf_cdc_t ), - .RstCfg ( RstCfg ) + .hyper_cmd_t ( hyper_cmd_t ) ) i_backend ( .clk_i ( clk_backend ), .clk_90_i ( clk_backend_90 ), .rst_ni ( rst_backend_n ), .test_mode_i ( test_mode_i ), - .cfg_apply_i ( cfg_backend_apply ), - .cfg_apply_valid_i ( cfg_backend_apply_valid ), - .cfg_apply_ready_o ( cfg_backend_apply_ready ), + .cfg_apply_i ( backend_cfg_apply ), + .cfg_apply_valid_i ( backend_cfg_apply_valid ), + .cfg_apply_ready_o ( backend_cfg_apply_ready ), .busy_o ( ), .tx_clk_delay_o ( ), .rx_o ( backend_rx ), @@ -240,12 +318,12 @@ module hyperbus_isochronous #( .tx_i ( backend_tx ), .tx_valid_i ( backend_tx_valid ), .tx_ready_o ( backend_tx_ready ), - .b_error_o ( backend_b_error ), - .b_valid_o ( backend_b_valid ), - .b_ready_i ( backend_b_ready ), - .trans_i ( backend_trans ), - .trans_valid_i ( backend_trans_valid ), - .trans_ready_o ( backend_trans_ready ), + .wrsp_error_o ( backend_wrsp_error ), + .wrsp_valid_o ( backend_wrsp_valid ), + .wrsp_ready_i ( backend_wrsp_ready ), + .cmd_i ( backend_cmd ), + .cmd_valid_i ( backend_cmd_valid ), + .cmd_ready_o ( backend_cmd_ready ), .hyper_cs_no ( hyper_cs_no ), .hyper_ck_o ( hyper_ck_o ), .hyper_ck_no ( hyper_ck_no ), diff --git a/src/hyperbus_midend.sv b/src/hyperbus_midend.sv new file mode 100644 index 0000000..c0fb55f --- /dev/null +++ b/src/hyperbus_midend.sv @@ -0,0 +1,850 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +`include "common_cells/registers.svh" +`include "common_cells/assertions.svh" + +module hyperbus_midend #( + parameter int unsigned HostAddrWidth = -1, + parameter int unsigned HostDataWidth = -1, + parameter int unsigned NumChips = -1, + parameter int unsigned NumPhys = -1, + parameter type host_req_t = logic, + parameter type host_w_t = logic, + parameter type host_r_t = logic, + parameter type host_wrsp_t = logic, + parameter type hyper_rx_t = logic, + parameter type hyper_tx_t = logic, + parameter type hyper_cmd_t = logic, + parameter type rule_t = logic +) ( + input logic clk_i, + input logic rst_ni, + + input host_req_t host_req_i, + input logic host_req_valid_i, + output logic host_req_ready_o, + + input host_w_t host_w_i, + input logic host_w_valid_i, + output logic host_w_ready_o, + + output host_r_t host_r_o, + output logic host_r_valid_o, + input logic host_r_ready_i, + + output host_wrsp_t host_wrsp_o, + output logic host_wrsp_valid_o, + input logic host_wrsp_ready_i, + + input hyperbus_pkg::frontend_cfg_t frontend_cfg_i, + input rule_t [NumChips-1:0] chip_rules_i, + output logic trans_active_o, + output logic decode_error_o, + + input hyper_rx_t rx_i, + input logic rx_valid_i, + output logic rx_ready_o, + + output hyper_tx_t tx_o, + output logic tx_valid_o, + input logic tx_ready_i, + + input logic wrsp_error_i, + input logic wrsp_valid_i, + output logic wrsp_ready_o, + + output hyper_cmd_t cmd_o, + output logic cmd_valid_o, + input logic cmd_ready_i +); + + localparam int unsigned HostDataBytes = HostDataWidth / 8; + localparam int unsigned HostBusAddrWidth = $clog2(HostDataBytes); + localparam int unsigned PhyDataWidth = NumPhys * 16; + localparam int unsigned PhyDataBytes = PhyDataWidth / 8; + localparam int unsigned ChipSelWidth = cf_math_pkg::idx_width(NumChips); + + `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= 8) + `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) + `ASSERT_INIT(HostAddrWidthValid, HostAddrWidth >= HostBusAddrWidth) + `ASSERT_INIT(HostDataWidthValid, + HostDataWidth >= PhyDataWidth && HostDataWidth <= 1024 && + (HostDataWidth & (HostDataWidth - 1)) == 0 && + (HostDataWidth % PhyDataWidth) == 0) + + typedef logic [HostAddrWidth-1:0] host_addr_t; + typedef logic [HostAddrWidth:0] host_ext_addr_t; + typedef logic [ChipSelWidth-1:0] chip_sel_idx_t; + + typedef enum logic [1:0] { + ErrorIdle, + ErrorRead, + ErrorWrite, + ErrorWriteResp + } error_state_e; + + typedef enum logic [2:0] { + AtomicIdle, + AtomicWaitWriteData, + AtomicReadCmd, + AtomicReadData, + AtomicWriteCmd, + AtomicWriteData, + AtomicWriteResp, + AtomicReturn + } atomic_state_e; + + typedef logic [HostDataWidth-1:0] host_data_t; + typedef logic [HostDataBytes-1:0] host_strb_t; + + chip_sel_idx_t cmd_chip_sel_idx; + logic cmd_fire; + logic converter_cmd_fire; + logic cmd_dec_valid; + logic cmd_dec_error; + logic req_range_valid; + logic req_decode_error; + logic error_req_fire; + error_state_e error_state_d, error_state_q; + hyperbus_pkg::hyper_blen_t error_beats_d, error_beats_q; + + host_r_t converted_host_r; + logic converted_host_r_valid; + logic converted_host_r_ready; + logic converted_host_w_ready; + host_w_t converted_host_w; + logic converted_host_w_valid; + + host_req_t cmd_req; + host_addr_t req_phy_first_addr; + host_ext_addr_t req_phy_first_ext; + host_ext_addr_t req_phy_end_addr; + host_ext_addr_t req_last_addr; + host_ext_addr_t req_phy_bytes; + hyperbus_pkg::hyper_blen_t req_phy_burst; + host_ext_addr_t range_cursor; + host_ext_addr_t cmd_rule_end_addr; + logic req_addr_overflow; + logic atomic_range_valid; + + host_req_t segment_req_d, segment_req_q; + hyperbus_pkg::hyper_blen_t segment_remaining_d, segment_remaining_q; + hyperbus_pkg::hyper_blen_t cmd_remaining; + host_ext_addr_t cmd_rule_capacity; + hyperbus_pkg::hyper_blen_t cmd_segment_burst; + host_addr_t cmd_next_addr; + logic segment_pending_d, segment_pending_q; + logic segment_final_d, segment_final_q; + logic segment_wrsp_error_d, segment_wrsp_error_q; + logic cmd_segment_final; + logic normal_cmd_fire; + logic normal_segment_complete; + host_req_t atomic_req_d, atomic_req_q; + host_w_t atomic_operand_d, atomic_operand_q; + host_w_t atomic_write_d, atomic_write_q; + host_r_t atomic_read_d, atomic_read_q; + hyperbus_pkg::hyper_resp_e atomic_resp_d, atomic_resp_q; + atomic_state_e atomic_state_d, atomic_state_q; + logic atomic_r_pending_d, atomic_r_pending_q; + logic atomic_b_pending_d, atomic_b_pending_q; + logic atomic_req_fire; + logic atomic_cmd_valid; + logic atomic_active; + logic normal_req; + logic atomic_request_valid; + host_data_t atomic_result; + host_data_t atomic_old_value; + host_data_t atomic_operand_value; + host_data_t atomic_swap_value; + host_data_t atomic_value_mask; + host_data_t atomic_result_value; + int unsigned atomic_byte_offset; + int unsigned atomic_operand_bytes; + int unsigned atomic_operand_bits; + + logic [PhyDataWidth-1:0] rx_data; + logic [PhyDataWidth/2-1:0] rx_data_lower_d; + logic [PhyDataWidth/2-1:0] rx_data_lower_q; + logic rx_error; + logic rx_last; + logic rx_valid; + logic rx_ready; + logic merge_r_d; + logic merge_r_q; + + logic [PhyDataWidth-1:0] tx_data; + logic [PhyDataBytes-1:0] tx_strb; + logic tx_last; + logic tx_valid; + logic tx_ready; + logic split_w_d; + logic split_w_q; + + logic trans_active_d; + logic trans_active_q; + logic trans_active_set; + logic trans_active_reset; + logic host_req_fire; + + assign atomic_active = atomic_state_q != AtomicIdle; + assign normal_req = host_req_i.atomic_op == hyperbus_pkg::HyperAtomicNone; + assign atomic_request_valid = + (host_req_i.atomic_op != hyperbus_pkg::HyperAtomicInvalid) && + (host_req_i.beats == 1) && + (host_req_i.burst == hyperbus_pkg::HyperBurstIncr) && + (host_req_i.size <= HostBusAddrWidth) && + ((host_req_i.atomic_op != hyperbus_pkg::HyperAtomicCompare) || + (host_req_i.size != '0)) && + atomic_range_valid; + assign atomic_cmd_valid = (atomic_state_q == AtomicReadCmd) || + (atomic_state_q == AtomicWriteCmd); + assign cmd_valid_o = atomic_cmd_valid || + segment_pending_q || + (host_req_valid_i && !trans_active_q && cmd_dec_valid && + req_range_valid && normal_req); + assign host_req_ready_o = !trans_active_q && + (normal_req ? + (req_decode_error || (cmd_dec_valid && cmd_ready_i)) : 1'b1); + assign host_req_fire = host_req_valid_i && host_req_ready_o; + assign cmd_fire = cmd_valid_o && cmd_ready_i; + assign normal_cmd_fire = cmd_fire && !atomic_cmd_valid; + assign converter_cmd_fire = atomic_cmd_valid ? cmd_fire : + (normal_cmd_fire && !segment_pending_q); + assign error_req_fire = host_req_valid_i && host_req_ready_o && req_decode_error; + assign atomic_req_fire = host_req_valid_i && host_req_ready_o && !normal_req; + assign decode_error_o = error_req_fire || + (atomic_req_fire && !atomic_range_valid); + assign trans_active_o = trans_active_q; + + addr_decode #( + .NoIndices ( NumChips ), + .NoRules ( NumChips ), + .addr_t ( host_addr_t ), + .rule_t ( rule_t ) + ) i_addr_decode_chip_sel ( + .addr_i ( req_phy_first_addr ), + .addr_map_i ( chip_rules_i ), + .idx_o ( cmd_chip_sel_idx ), + .dec_valid_o ( cmd_dec_valid ), + .dec_error_o ( cmd_dec_error ), + .en_default_idx_i ( 1'b0 ), + .default_idx_i ( '0 ) + ); + + always_comb begin : proc_cmd_cs + cmd_o.cs = '0; + if (cmd_dec_valid) begin + cmd_o.cs[cmd_chip_sel_idx] = 1'b1; + end + end + + always_comb begin : proc_cmd_req + cmd_req = host_req_i; + if (segment_pending_q) begin + cmd_req = segment_req_q; + end + if (atomic_active) begin + cmd_req = atomic_req_q; + cmd_req.write = atomic_state_q != AtomicReadCmd; + end + if (cmd_req.atomic_op != hyperbus_pkg::HyperAtomicNone) begin + cmd_req.beats = 1; + if ((cmd_req.atomic_op == hyperbus_pkg::HyperAtomicCompare) && + (cmd_req.size != '0)) begin + cmd_req.size = cmd_req.size - 1'b1; + end + end + end + + // Cover every aligned PHY group touched by the host transaction. + always_comb begin : proc_req_phy_range + req_phy_first_addr = (cmd_req.addr >> NumPhys) << NumPhys; + req_phy_first_ext = host_ext_addr_t'(req_phy_first_addr); + req_last_addr = (host_ext_addr_t'(cmd_req.addr >> cmd_req.size) << cmd_req.size) + + (host_ext_addr_t'(cmd_req.beats) << cmd_req.size); + req_phy_bytes = ((req_last_addr - req_phy_first_ext) + + ((host_ext_addr_t'(1) << NumPhys) - 1)) >> NumPhys; + req_phy_bytes = req_phy_bytes << NumPhys; + req_phy_burst = hyperbus_pkg::hyper_blen_t'(req_phy_bytes >> 1); + req_phy_end_addr = req_phy_first_ext + req_phy_bytes; + req_addr_overflow = (req_last_addr > (host_ext_addr_t'(1) << HostAddrWidth)) || + (req_phy_end_addr > (host_ext_addr_t'(1) << HostAddrWidth)); + end + + // Follow decoder priority across the request and find the first segment boundary. + always_comb begin : proc_req_rule_range + range_cursor = req_phy_first_ext; + cmd_rule_end_addr = req_phy_first_ext; + req_range_valid = !req_addr_overflow; + + for (int unsigned step = 0; step < (2 * NumChips); step++) begin + logic rule_found; + int unsigned selected_rule_idx; + host_ext_addr_t selected_rule_end; + host_ext_addr_t next_transition; + + rule_found = 1'b0; + selected_rule_idx = 0; + selected_rule_end = range_cursor; + for (int unsigned rule_idx = 0; rule_idx < NumChips; rule_idx++) begin + host_ext_addr_t rule_end; + + rule_end = (chip_rules_i[rule_idx].end_addr == '0) ? + (host_ext_addr_t'(1) << HostAddrWidth) : + host_ext_addr_t'(chip_rules_i[rule_idx].end_addr); + if ((range_cursor >= host_ext_addr_t'(chip_rules_i[rule_idx].start_addr)) && + (range_cursor < rule_end)) begin + rule_found = 1'b1; + selected_rule_idx = rule_idx; + selected_rule_end = rule_end; + end + end + next_transition = selected_rule_end; + for (int unsigned rule_idx = 0; rule_idx < NumChips; rule_idx++) begin + host_ext_addr_t rule_start; + + rule_start = host_ext_addr_t'(chip_rules_i[rule_idx].start_addr); + if ((rule_idx > selected_rule_idx) && (rule_start > range_cursor) && + (rule_start < next_transition)) begin + next_transition = rule_start; + end + end + if (step == 0) begin + cmd_rule_end_addr = next_transition; + end + if (range_cursor < req_phy_end_addr) begin + if (rule_found) begin + if ((req_phy_end_addr > next_transition) && + (next_transition[NumPhys-1:0] != '0)) begin + req_range_valid = 1'b0; + end + range_cursor = (req_phy_end_addr < next_transition) ? + req_phy_end_addr : next_transition; + end else begin + req_range_valid = 1'b0; + end + end + end + if (range_cursor < req_phy_end_addr) begin + req_range_valid = 1'b0; + end + end + + assign atomic_range_valid = cmd_dec_valid && !req_addr_overflow && + (req_phy_end_addr <= cmd_rule_end_addr); + + assign req_decode_error = normal_req && + (cmd_dec_error || + (!atomic_active && !segment_pending_q && !req_range_valid)); + + assign cmd_o.trans.write = cmd_req.write; + assign cmd_o.trans.burst_type = 1'b1; // Wrapping HyperBus bursts are not supported. + assign cmd_o.trans.address_space = frontend_cfg_i.address_space; + assign cmd_o.trans.address = (NumPhys == 2) ? + (frontend_cfg_i.dual_phy ? + ((req_phy_first_addr & + ((host_addr_t'(1) << frontend_cfg_i.address_mask_msb) - 1)) >> 2) : + (((req_phy_first_addr & + ((host_addr_t'(1) << frontend_cfg_i.address_mask_msb) - 1)) >> 2) << 1)) : + ((req_phy_first_addr & + ((host_addr_t'(1) << frontend_cfg_i.address_mask_msb) - 1)) >> 1); + + always_comb begin : proc_cmd_segment + cmd_remaining = segment_pending_q ? segment_remaining_q : req_phy_burst; + cmd_rule_capacity = host_ext_addr_t'(cmd_remaining); + if (cmd_dec_valid) begin + cmd_rule_capacity = (cmd_rule_end_addr - req_phy_first_ext) >> 1; + end + cmd_segment_burst = cmd_remaining; + if (cmd_rule_capacity < host_ext_addr_t'(cmd_remaining)) begin + cmd_segment_burst = hyperbus_pkg::hyper_blen_t'(cmd_rule_capacity); + end + if (atomic_cmd_valid) begin + cmd_segment_burst = req_phy_burst; + end + + cmd_segment_final = atomic_cmd_valid || (cmd_segment_burst == cmd_remaining); + cmd_next_addr = host_addr_t'(req_phy_first_ext + + (host_ext_addr_t'(cmd_segment_burst) << 1)); + cmd_o.trans.burst = cmd_segment_burst; + end + + always_comb begin : proc_rx_merge + rx_valid = rx_valid_i; + rx_ready_o = rx_ready; + rx_data = rx_i.data; + rx_last = rx_i.last; + rx_error = rx_i.error; + rx_data_lower_d = rx_data_lower_q; + merge_r_d = merge_r_q; + + if ((NumPhys == 2) && !frontend_cfg_i.dual_phy) begin + if (rx_valid_i && rx_ready) begin + merge_r_d = merge_r_q + 1; + end + rx_data = {rx_i.data[PhyDataWidth/2-1:0], rx_data_lower_q}; + rx_valid = rx_valid_i && merge_r_q; + rx_last = rx_i.last && merge_r_q; + rx_error = rx_i.error; + if (!merge_r_q) begin + rx_data_lower_d = rx_i.data[PhyDataWidth/2-1:0]; + end + end + if (!atomic_active) begin + rx_last = rx_last && segment_final_q; + end + end + + hyperbus_phy2r #( + .HostDataWidth ( HostDataWidth ), + .BurstLength ( hyperbus_pkg::HyperBurstWidth ), + .T ( host_r_t ), + .NumPhys ( NumPhys ) + ) i_hyperbus_phy2r ( + .clk_i, + .rst_ni, + .size_i ( cmd_req.size ), + .cmd_fire_i ( converter_cmd_fire ), + .start_addr_i ( cmd_req.addr[HostBusAddrWidth-1:0] ), + .burst_len_i ( cmd_req.beats ), + .is_read_i ( !cmd_req.write ), + .phy_valid_i ( rx_valid ), + .phy_ready_o ( rx_ready ), + .data_i ( rx_data ), + .last_i ( rx_last ), + .error_i ( rx_error ), + .host_valid_o ( converted_host_r_valid ), + .host_ready_i ( converted_host_r_ready ), + .data_o ( converted_host_r ) + ); + + `FFARN(merge_r_q, merge_r_d, 1'b0, clk_i, rst_ni) + `FFARN(rx_data_lower_q, rx_data_lower_d, '0, clk_i, rst_ni) + + hyperbus_w2phy #( + .HostDataWidth ( HostDataWidth ), + .T ( host_w_t ), + .NumPhys ( NumPhys ) + ) i_hyperbus_w2phy ( + .clk_i, + .rst_ni, + .size_i ( cmd_req.size ), + .is_write_i ( cmd_req.write ), + .cmd_fire_i ( converter_cmd_fire ), + .start_addr_i ( cmd_req.addr[HostBusAddrWidth-1:0] ), + .data_i ( converted_host_w ), + .host_valid_i ( converted_host_w_valid ), + .host_ready_o ( converted_host_w_ready ), + .data_o ( tx_data ), + .last_o ( tx_last ), + .strb_o ( tx_strb ), + .phy_valid_o ( tx_valid ), + .phy_ready_i ( tx_ready ) + ); + + always_comb begin : proc_tx_split + tx_o.data = tx_data; + tx_o.strb = tx_strb; + tx_o.last = tx_last; + tx_valid_o = tx_valid; + tx_ready = tx_ready_i; + split_w_d = split_w_q; + + if ((NumPhys == 2) && !frontend_cfg_i.dual_phy) begin + if (tx_valid && tx_ready_i) begin + split_w_d = split_w_q + 1; + end + tx_o.last = tx_last && split_w_q; + tx_valid_o = tx_valid; + tx_ready = tx_ready_i && split_w_q; + tx_o.data = {tx_data[PhyDataWidth/2-1:0], tx_data[PhyDataWidth/2-1:0]}; + tx_o.strb = {tx_strb[NumPhys-1:0], tx_strb[NumPhys-1:0]}; + if (split_w_q) begin + tx_o.data = {tx_data[PhyDataWidth-1:PhyDataWidth/2], + tx_data[PhyDataWidth-1:PhyDataWidth/2]}; + tx_o.strb = {tx_strb[NumPhys*2-1:NumPhys], tx_strb[NumPhys*2-1:NumPhys]}; + end + end + end + + `FFARN(split_w_q, split_w_d, 1'b0, clk_i, rst_ni) + + always_comb begin : proc_atomic_alu + atomic_byte_offset = 0; + atomic_operand_bytes = 1; + atomic_operand_bits = 8; + atomic_value_mask = '1; + atomic_old_value = atomic_read_q.data; + atomic_operand_value = atomic_operand_q.data; + atomic_swap_value = atomic_operand_q.data >> 8; + atomic_result_value = atomic_old_value; + atomic_result = atomic_read_q.data; + + // Malformed atomics are drained and returned without evaluating invalid widths. + if (atomic_resp_q == hyperbus_pkg::HyperRespOkay) begin + atomic_byte_offset = unsigned'(atomic_req_q.addr[HostBusAddrWidth-1:0]); + atomic_operand_bytes = 1 << ((atomic_req_q.atomic_op == + hyperbus_pkg::HyperAtomicCompare) ? + (atomic_req_q.size - 1'b1) : atomic_req_q.size); + atomic_operand_bits = atomic_operand_bytes * 8; + + if (atomic_operand_bits < HostDataWidth) begin + atomic_value_mask = atomic_value_mask >> (HostDataWidth - atomic_operand_bits); + end + + atomic_old_value = atomic_read_q.data >> (atomic_byte_offset * 8); + atomic_operand_value = atomic_operand_q.data >> (atomic_byte_offset * 8); + atomic_swap_value = atomic_operand_value >> atomic_operand_bits; + atomic_result_value = atomic_old_value; + + unique case (atomic_req_q.atomic_op) + hyperbus_pkg::HyperAtomicSwap: begin + atomic_result_value = atomic_operand_value; + end + hyperbus_pkg::HyperAtomicCompare: begin + if ((atomic_old_value & atomic_value_mask) == + (atomic_operand_value & atomic_value_mask)) begin + atomic_result_value = atomic_swap_value; + end + end + hyperbus_pkg::HyperAtomicAdd: begin + atomic_result_value = atomic_old_value + atomic_operand_value; + end + hyperbus_pkg::HyperAtomicClear: begin + atomic_result_value = atomic_old_value & ~atomic_operand_value; + end + hyperbus_pkg::HyperAtomicXor: begin + atomic_result_value = atomic_old_value ^ atomic_operand_value; + end + hyperbus_pkg::HyperAtomicSet: begin + atomic_result_value = atomic_old_value | atomic_operand_value; + end + hyperbus_pkg::HyperAtomicSignedMax: begin + if (atomic_old_value[atomic_operand_bits-1] != + atomic_operand_value[atomic_operand_bits-1]) begin + if (atomic_old_value[atomic_operand_bits-1]) begin + atomic_result_value = atomic_operand_value; + end + end else if ((atomic_old_value & atomic_value_mask) < + (atomic_operand_value & atomic_value_mask)) begin + atomic_result_value = atomic_operand_value; + end + end + hyperbus_pkg::HyperAtomicSignedMin: begin + if (atomic_old_value[atomic_operand_bits-1] != + atomic_operand_value[atomic_operand_bits-1]) begin + if (atomic_operand_value[atomic_operand_bits-1]) begin + atomic_result_value = atomic_operand_value; + end + end else if ((atomic_old_value & atomic_value_mask) > + (atomic_operand_value & atomic_value_mask)) begin + atomic_result_value = atomic_operand_value; + end + end + hyperbus_pkg::HyperAtomicUnsignedMax: begin + if ((atomic_old_value & atomic_value_mask) < + (atomic_operand_value & atomic_value_mask)) begin + atomic_result_value = atomic_operand_value; + end + end + hyperbus_pkg::HyperAtomicUnsignedMin: begin + if ((atomic_old_value & atomic_value_mask) > + (atomic_operand_value & atomic_value_mask)) begin + atomic_result_value = atomic_operand_value; + end + end + default:; + endcase + + atomic_result = atomic_read_q.data & + ~(atomic_value_mask << (atomic_byte_offset * 8)); + atomic_result |= (atomic_result_value & atomic_value_mask) << + (atomic_byte_offset * 8); + end + end + + always_comb begin : proc_atomic_control + atomic_state_d = atomic_state_q; + atomic_req_d = atomic_req_q; + atomic_operand_d = atomic_operand_q; + atomic_write_d = atomic_write_q; + atomic_read_d = atomic_read_q; + atomic_resp_d = atomic_resp_q; + atomic_r_pending_d = atomic_r_pending_q; + atomic_b_pending_d = atomic_b_pending_q; + + unique case (atomic_state_q) + AtomicIdle: begin + if (atomic_req_fire) begin + atomic_req_d = host_req_i; + atomic_resp_d = hyperbus_pkg::HyperRespOkay; + if (!atomic_request_valid) begin + atomic_resp_d = hyperbus_pkg::HyperRespAtomicError; + end + atomic_state_d = AtomicWaitWriteData; + end + end + AtomicWaitWriteData: begin + if (host_w_valid_i && host_w_ready_o) begin + atomic_operand_d = host_w_i; + if (atomic_resp_q == hyperbus_pkg::HyperRespOkay) begin + atomic_state_d = AtomicReadCmd; + end else if (host_w_i.last) begin + atomic_r_pending_d = atomic_req_q.atomic_return; + atomic_b_pending_d = 1'b1; + atomic_state_d = AtomicReturn; + end + end + end + AtomicReadCmd: begin + if (cmd_fire) begin + atomic_state_d = AtomicReadData; + end + end + AtomicReadData: begin + if (converted_host_r_valid && converted_host_r_ready) begin + atomic_read_d = converted_host_r; + if (converted_host_r.resp != hyperbus_pkg::HyperRespOkay) begin + atomic_resp_d = converted_host_r.resp; + atomic_r_pending_d = atomic_req_q.atomic_return; + atomic_b_pending_d = 1'b1; + atomic_state_d = AtomicReturn; + end else begin + atomic_state_d = AtomicWriteCmd; + end + end + end + AtomicWriteCmd: begin + atomic_write_d.data = atomic_result; + atomic_write_d.strb = '0; + atomic_write_d.last = 1'b1; + for (int unsigned i = 0; i < HostDataBytes; i++) begin + if ((i >= atomic_byte_offset) && + (i < atomic_byte_offset + atomic_operand_bytes)) begin + atomic_write_d.strb[i] = 1'b1; + end + end + if (cmd_fire) begin + atomic_state_d = AtomicWriteData; + end + end + AtomicWriteData: begin + if (converted_host_w_valid && converted_host_w_ready) begin + atomic_state_d = AtomicWriteResp; + end + end + AtomicWriteResp: begin + if (wrsp_valid_i && wrsp_ready_o) begin + atomic_resp_d = wrsp_error_i ? hyperbus_pkg::HyperRespAccessError : + hyperbus_pkg::HyperRespOkay; + atomic_r_pending_d = atomic_req_q.atomic_return; + atomic_b_pending_d = 1'b1; + atomic_state_d = AtomicReturn; + end + end + AtomicReturn: begin + if (host_r_valid_o && host_r_ready_i) begin + atomic_r_pending_d = 1'b0; + end + if (host_wrsp_valid_o && host_wrsp_ready_i) begin + atomic_b_pending_d = 1'b0; + end + if ((!atomic_r_pending_q || (host_r_valid_o && host_r_ready_i)) && + (!atomic_b_pending_q || (host_wrsp_valid_o && host_wrsp_ready_i))) begin + atomic_state_d = AtomicIdle; + end + end + default: begin + atomic_state_d = AtomicIdle; + end + endcase + end + + `FFARN(atomic_state_q, atomic_state_d, AtomicIdle, clk_i, rst_ni) + `FFARN(atomic_req_q, atomic_req_d, '0, clk_i, rst_ni) + `FFARN(atomic_operand_q, atomic_operand_d, '0, clk_i, rst_ni) + `FFARN(atomic_write_q, atomic_write_d, '0, clk_i, rst_ni) + `FFARN(atomic_read_q, atomic_read_d, '0, clk_i, rst_ni) + `FFARN(atomic_resp_q, atomic_resp_d, hyperbus_pkg::HyperRespOkay, clk_i, rst_ni) + `FFARN(atomic_r_pending_q, atomic_r_pending_d, 1'b0, clk_i, rst_ni) + `FFARN(atomic_b_pending_q, atomic_b_pending_d, 1'b0, clk_i, rst_ni) + + assign normal_segment_complete = trans_active_q && !atomic_active && + (segment_req_q.write ? (wrsp_valid_i && wrsp_ready_o) : + (rx_valid_i && rx_ready_o && rx_i.last)); + + always_comb begin : proc_segments + segment_req_d = segment_req_q; + segment_remaining_d = segment_remaining_q; + segment_pending_d = segment_pending_q; + segment_final_d = segment_final_q; + segment_wrsp_error_d = segment_wrsp_error_q; + + if (normal_segment_complete && !segment_final_q) begin + segment_pending_d = 1'b1; + if (segment_req_q.write && wrsp_error_i) begin + segment_wrsp_error_d = 1'b1; + end + end + if (normal_cmd_fire) begin + segment_pending_d = 1'b0; + segment_final_d = cmd_segment_final; + segment_req_d = cmd_req; + segment_req_d.addr = cmd_next_addr; + segment_remaining_d = cmd_remaining - cmd_segment_burst; + if (!trans_active_q) begin + segment_wrsp_error_d = 1'b0; + end + end + end + + `FFARN(segment_req_q, segment_req_d, '0, clk_i, rst_ni) + `FFARN(segment_remaining_q, segment_remaining_d, '0, clk_i, rst_ni) + `FFARN(segment_pending_q, segment_pending_d, 1'b0, clk_i, rst_ni) + `FFARN(segment_final_q, segment_final_d, 1'b1, clk_i, rst_ni) + `FFARN(segment_wrsp_error_q, segment_wrsp_error_d, 1'b0, clk_i, rst_ni) + + always_comb begin : proc_error_control + error_state_d = error_state_q; + error_beats_d = error_beats_q; + + unique case (error_state_q) + ErrorIdle: begin + if (error_req_fire) begin + error_beats_d = host_req_i.beats; + error_state_d = host_req_i.write ? ErrorWrite : ErrorRead; + end + end + ErrorRead: begin + if (host_r_valid_o && host_r_ready_i) begin + error_beats_d = error_beats_q - 1'b1; + if (error_beats_q == 1) begin + error_state_d = ErrorIdle; + end + end + end + ErrorWrite: begin + if (host_w_valid_i && host_w_ready_o && host_w_i.last) begin + error_state_d = ErrorWriteResp; + end + end + ErrorWriteResp: begin + if (host_wrsp_valid_o && host_wrsp_ready_i) begin + error_state_d = ErrorIdle; + end + end + default: begin + error_state_d = ErrorIdle; + end + endcase + end + + always_comb begin : proc_host_mux + converted_host_w = host_w_i; + converted_host_w_valid = host_w_valid_i; + host_w_ready_o = converted_host_w_ready; + host_r_o = converted_host_r; + host_r_valid_o = converted_host_r_valid; + converted_host_r_ready = host_r_ready_i; + host_wrsp_o.resp = (wrsp_error_i || segment_wrsp_error_q) ? + hyperbus_pkg::HyperRespAccessError : + hyperbus_pkg::HyperRespOkay; + host_wrsp_o.atomic_ok = 1'b0; + host_wrsp_valid_o = wrsp_valid_i && segment_final_q; + wrsp_ready_o = segment_final_q ? host_wrsp_ready_i : 1'b1; + + if (atomic_active) begin + converted_host_w = '0; + converted_host_w_valid = 1'b0; + host_w_ready_o = 1'b0; + host_r_o = '0; + host_r_valid_o = 1'b0; + converted_host_r_ready = 1'b0; + host_wrsp_o = '0; + host_wrsp_valid_o = 1'b0; + wrsp_ready_o = 1'b0; + + unique case (atomic_state_q) + AtomicWaitWriteData: begin + host_w_ready_o = 1'b1; + end + AtomicReadData: begin + converted_host_r_ready = 1'b1; + end + AtomicWriteData: begin + converted_host_w = atomic_write_q; + converted_host_w_valid = 1'b1; + end + AtomicWriteResp: begin + wrsp_ready_o = 1'b1; + end + AtomicReturn: begin + host_r_o = atomic_read_q; + host_r_o.resp = atomic_resp_q; + host_r_o.last = 1'b1; + host_r_o.atomic_ok = atomic_resp_q == hyperbus_pkg::HyperRespOkay; + host_r_valid_o = atomic_r_pending_q; + host_wrsp_o.resp = atomic_resp_q; + host_wrsp_o.atomic_ok = atomic_resp_q == hyperbus_pkg::HyperRespOkay; + host_wrsp_valid_o = atomic_b_pending_q; + end + default:; + endcase + end + + if (error_state_q != ErrorIdle) begin + converted_host_w = '0; + converted_host_w_valid = 1'b0; + host_w_ready_o = error_state_q == ErrorWrite; + host_r_o = '0; + host_r_o.resp = hyperbus_pkg::HyperRespDecodeError; + host_r_o.last = error_beats_q == 1; + host_r_valid_o = error_state_q == ErrorRead; + converted_host_r_ready = 1'b0; + host_wrsp_o = '0; + host_wrsp_o.resp = hyperbus_pkg::HyperRespDecodeError; + host_wrsp_valid_o = error_state_q == ErrorWriteResp; + wrsp_ready_o = 1'b0; + end + end + + `FFARN(error_state_q, error_state_d, ErrorIdle, clk_i, rst_ni) + `FFARN(error_beats_q, error_beats_d, '0, clk_i, rst_ni) + + assign trans_active_set = (normal_cmd_fire && !segment_pending_q) || + error_req_fire || atomic_req_fire; + assign trans_active_reset = atomic_active ? + ((atomic_state_q == AtomicReturn) && + (!atomic_r_pending_q || (host_r_valid_o && host_r_ready_i)) && + (!atomic_b_pending_q || (host_wrsp_valid_o && host_wrsp_ready_i))) : + ((host_r_valid_o && host_r_ready_i && host_r_o.last) || + (host_wrsp_valid_o && host_wrsp_ready_i)); + + always_comb begin : proc_trans_active + trans_active_d = trans_active_q; + + if (trans_active_reset) begin + trans_active_d = 1'b0; + end + if (trans_active_set) begin + trans_active_d = 1'b1; + end + end + + `FFARN(trans_active_q, trans_active_d, 1'b0, clk_i, rst_ni) + + `ASSERT_KNOWN_IF(HostReqKnown, host_req_i, host_req_fire) + `ASSERT(HostReqBeatsNonzero, host_req_fire |-> (host_req_i.beats != '0)) + `ASSERT(HostReqSizeValid, host_req_fire |-> (host_req_i.size <= HostBusAddrWidth)) + `ASSERT(HostReqBurstValid, host_req_fire |-> + ((host_req_i.burst == hyperbus_pkg::HyperBurstIncr) || + ((host_req_i.burst == hyperbus_pkg::HyperBurstFixed) && (host_req_i.beats == 1)))) + `ASSERT(HostReqAtomicValid, host_req_fire |-> + (host_req_i.atomic_op <= hyperbus_pkg::HyperAtomicUnsignedMin)) + `ASSERT(HostReqAtomicWrite, (host_req_fire && + (host_req_i.atomic_op != hyperbus_pkg::HyperAtomicNone)) |-> host_req_i.write) + `ASSERT(BackendBurstNonzero, cmd_fire |-> (cmd_o.trans.burst != '0)) + +endmodule diff --git a/src/hyperbus_phy.sv b/src/hyperbus_phy.sv index cf22b44..802d7ff 100644 --- a/src/hyperbus_phy.sv +++ b/src/hyperbus_phy.sv @@ -60,15 +60,16 @@ module hyperbus_phy import hyperbus_pkg::*; #( ); localparam int unsigned RxFifoDepth = 2 ** RxFifoLogDepth; - // Stop before the RWDS CDC FIFO can become completely full. The extra - // margin covers pointer synchronization and the delayed effect of CK gating. - localparam int unsigned RxFifoStopMargin = SyncStages + 2; + // r_outstand_q includes samples still crossing the RWDS CDC, so its limit + // must not include the synchronizer depth again. Reserve two entries for + // stopping CK and the final RWDS capture at the clock-gating boundary. + localparam int unsigned RxFifoStopMargin = 2; localparam int unsigned RxOutstandingLimit = (RxFifoDepth > RxFifoStopMargin) ? (RxFifoDepth - RxFifoStopMargin) : 1; - logic [1:0] phys_in_use; + logic [1:0] words_per_beat; - assign phys_in_use = (NumPhys==2) ? (cfg_i.phys_in_use + 1) : 1; + assign words_per_beat = (NumPhys == 2 && cfg_i.dual_phy) ? 2 : 1; // PHY state hyper_phy_state_t state_d, state_q; @@ -234,7 +235,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( // cfg_i.chip.en_latency_additional overwrites the sampled RWDS value. assign ctl_add_latency = trx_rwds_sample | cfg_i.chip.en_latency_additional; - assign ctl_tf_burst_last = (tf_q.burst == 1) || (tf_q.burst == phys_in_use); + assign ctl_tf_burst_last = (tf_q.burst == 1) || (tf_q.burst == words_per_beat); assign ctl_tf_burst_done = (tf_q.burst == 0); assign ctl_timer_rwr_done = (timer_q <= 3); @@ -384,7 +385,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( if (ctl_rclk_ena) begin trx_clk_ena = 1'b1; r_outstand_inc = 1'b1; - tf_d.burst = tf_q.burst - phys_in_use; + tf_d.burst = tf_q.burst - words_per_beat; tf_d.address = tf_q.address + 1; if (ctl_tf_burst_last) begin timer_d = cfg_i.chip.t_csh_cycles; @@ -405,7 +406,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( // Dataflow handled outside FSM if (ctl_wclk_ena) begin trx_clk_ena = 1'b1; - tf_d.burst = tf_q.burst - phys_in_use; + tf_d.burst = tf_q.burst - words_per_beat; tf_d.address = tf_q.address + 1; if (ctl_tf_burst_last) begin b_pending_set = 1'b1; @@ -449,7 +450,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( // PHY state registers, including timer and transfer always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_phy - if (~rst_ni) begin + if (!rst_ni) begin state_q <= Startup; timer_q <= StartupCycles; tf_q <= hyper_tf_t'{burst_type: 1'b1, default:'0}; diff --git a/src/hyperbus_phy2r.sv b/src/hyperbus_phy2r.sv index f66eabc..49a7921 100644 --- a/src/hyperbus_phy2r.sv +++ b/src/hyperbus_phy2r.sv @@ -4,156 +4,160 @@ // // Luca Valente +`include "common_cells/registers.svh" + module hyperbus_phy2r #( - parameter int unsigned AxiDataWidth = -1, - parameter int unsigned NumPhys = -1, - parameter type T = logic, - parameter int unsigned BurstLength = -1, - parameter int unsigned AddrWidth = $clog2(AxiDataWidth/8) + parameter int unsigned HostDataWidth = -1, + parameter int unsigned NumPhys = -1, + parameter type T = logic, + parameter int unsigned BurstLength = -1, + parameter int unsigned AddrWidth = $clog2(HostDataWidth / 8) ) ( - input logic clk_i, - input logic rst_ni, - input logic [2:0] size, - input logic is_a_read, - input logic trans_handshake, - input logic [AddrWidth-1:0] start_addr, - input logic [BurstLength-1:0] burst_len, - output logic axi_valid_o, - input logic axi_ready_i, - output T data_o, - input logic phy_valid_i, - output logic phy_ready_o, - input logic [16*NumPhys-1:0] data_i, - input logic last_i, - input logic error_i + input logic clk_i, + input logic rst_ni, + input logic [2:0] size_i, + input logic is_read_i, + input logic cmd_fire_i, + input logic [AddrWidth-1:0] start_addr_i, + input logic [BurstLength-1:0] burst_len_i, + output logic host_valid_o, + input logic host_ready_i, + output T data_o, + input logic phy_valid_i, + output logic phy_ready_o, + input logic [16*NumPhys-1:0] data_i, + input logic last_i, + input logic error_i ); - // Cutting the combinatorial path between cdc fifo and AXI Master - typedef enum logic [2:0] { - Idle, - WaitData, - Sample, - CntReady - } hyper_splitter_state_t; - - hyper_splitter_state_t state_d, state_q; - - localparam int unsigned NumAxiBytes = AxiDataWidth/8; - localparam int unsigned NumPhyBytes = NumPhys*2; - localparam int unsigned AxiBytesInPhyBeat = NumAxiBytes/NumPhyBytes; - localparam int unsigned WordCntWidth = (AxiBytesInPhyBeat==1) ? 1 : $clog2(AxiBytesInPhyBeat); + localparam int unsigned NumHostBytes = HostDataWidth / 8; + localparam int unsigned NumPhyBytes = NumPhys * 2; + localparam int unsigned PhyBeatsPerHost = NumHostBytes / NumPhyBytes; + localparam int unsigned WordCntWidth = + (PhyBeatsPerHost == 1) ? 1 : $clog2(PhyBeatsPerHost); - logic [BurstLength-1:0] byte_axi_addr_d, byte_axi_addr_q; - logic [BurstLength-1:0] byte_phy_cnt_d, byte_phy_cnt_q; - logic [BurstLength-1:0] last_addr_d, last_addr_q; + typedef enum logic [2:0] { + Idle, + WaitData, + Sample, + CntReady + } hyper_splitter_state_e; - logic [3:0] size_d, size_q; - T data_buffer_d, data_buffer_q; + hyper_splitter_state_e state_d, state_q; + logic [BurstLength-1:0] byte_host_addr_d, byte_host_addr_q; + logic [BurstLength-1:0] byte_phy_cnt_d, byte_phy_cnt_q; + logic [BurstLength-1:0] last_addr_d, last_addr_q; + logic [3:0] size_d, size_q; + T data_buffer_d, data_buffer_q; + logic [WordCntWidth-1:0] word_cnt; + logic enough_data; + logic enough_data_q; + logic sent_available_data; + logic [BurstLength-1:0] next_host_addr; + logic host_last; - logic is_16_bw, is_8_bw; - logic [WordCntWidth-1:0] word_cnt; - logic enough_data; - logic enough_data_q; - logic sent_available_data; - logic [BurstLength-1:0] next_axi_addr; - logic axi_last; + assign word_cnt = (PhyBeatsPerHost == 1) ? '0 : + byte_phy_cnt_q[($clog2(NumPhys) + 1) +: WordCntWidth]; + assign next_host_addr = ((byte_host_addr_q >> size_q) << size_q) + (1 << size_q); + assign enough_data = byte_phy_cnt_d >= next_host_addr; + assign enough_data_q = byte_phy_cnt_q >= next_host_addr; + assign sent_available_data = byte_host_addr_d >= byte_phy_cnt_q; + assign host_last = data_buffer_q.last && (last_addr_q == next_host_addr); - assign word_cnt = (AxiBytesInPhyBeat==1) ? '0 : byte_phy_cnt_q[($clog2(NumPhys)+1) +:WordCntWidth]; - assign next_axi_addr = ((byte_axi_addr_q>>size_d)<< size_d) + (1<= next_axi_addr; - assign enough_data_q = byte_phy_cnt_q >= next_axi_addr; - assign sent_available_data = byte_axi_addr_d >= byte_phy_cnt_q; - assign axi_last = data_buffer_q.last && (last_addr_q == next_axi_addr); - assign data_o.data = data_buffer_q.data; - assign data_o.error = data_buffer_q.error; - assign data_o.valid = '0; - assign data_o.last = axi_last; + assign data_o.data = data_buffer_q.data; + assign data_o.resp = data_buffer_q.resp; + assign data_o.last = host_last; + assign data_o.atomic_ok = 1'b0; - always_comb begin : counter - byte_axi_addr_d = byte_axi_addr_q; - size_d = size_q; - byte_phy_cnt_d = byte_phy_cnt_q; - last_addr_d = last_addr_q; - if (trans_handshake & is_a_read) begin - byte_axi_addr_d[BurstLength-1:AddrWidth] = '0; - byte_axi_addr_d[AddrWidth-1:0] = start_addr; - size_d = size; - byte_phy_cnt_d[BurstLength-1:AddrWidth] = '0; - byte_phy_cnt_d[AddrWidth-1:0] = (start_addr>>NumPhys)<>size)<>size_q)<< size_q) + (1<> NumPhys) << NumPhys; + last_addr_d = ((start_addr_i >> size_i) << size_i) + (burst_len_i << size_i); + size_d = size_i; end - WaitData: begin - phy_ready_o = 1'b1; - if (phy_valid_i) begin - state_d = Sample; - end + if (host_valid_o && host_ready_i) begin + byte_host_addr_d = ((byte_host_addr_q >> size_q) << size_q) + (1 << size_q); end - Sample: begin - phy_ready_o = 1'b1; - if(enough_data) begin - state_d = CntReady; - end + if (phy_valid_i && phy_ready_o) begin + byte_phy_cnt_d = byte_phy_cnt_q + NumPhys * 2; end - CntReady: begin - axi_valid_o = enough_data_q; - phy_ready_o = !enough_data_q; - if(axi_valid_o && sent_available_data) begin - phy_ready_o = 1'b1; - state_d = (data_o.last) ? Idle : ( phy_valid_i ? Sample : WaitData ); - end else if (axi_valid_o && (last_addr_q==byte_axi_addr_d)) begin - state_d = Idle; - end + end + + always_comb begin : proc_sample + data_buffer_d = data_buffer_q; + + if (state_d == Idle) begin + data_buffer_d.last = 1'b0; + data_buffer_d.data = '0; + data_buffer_d.resp = hyperbus_pkg::HyperRespOkay; + end else begin + if (host_valid_o && host_ready_i) begin + data_buffer_d.resp = hyperbus_pkg::HyperRespOkay; + end + if (phy_ready_o && phy_valid_i) begin + data_buffer_d.data[word_cnt*(16*NumPhys) +: (16*NumPhys)] = data_i; + if (error_i) begin + data_buffer_d.resp = hyperbus_pkg::HyperRespAccessError; + end + data_buffer_d.last = last_i; + end end - endcase - end + end + + always_comb begin : proc_fsm + state_d = state_q; + host_valid_o = 1'b0; + phy_ready_o = 1'b0; - always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_phy - if (~rst_ni) begin - data_buffer_q <= '0; - state_q <= Idle; - byte_axi_addr_q <= '0; - byte_phy_cnt_q <= '0; - size_q <= '0; - last_addr_q <= '0; - end else begin - state_q <= state_d; - data_buffer_q <= data_buffer_d; - byte_axi_addr_q <= byte_axi_addr_d; - byte_phy_cnt_q <= byte_phy_cnt_d; - size_q <= size_d; - last_addr_q <= last_addr_d; - end - end + unique case (state_q) + Idle: begin + if (cmd_fire_i && is_read_i) begin + state_d = WaitData; + end + end + WaitData: begin + phy_ready_o = 1'b1; + if (phy_valid_i) begin + state_d = Sample; + end + end + Sample: begin + phy_ready_o = 1'b1; + if (enough_data) begin + state_d = CntReady; + end + end + CntReady: begin + host_valid_o = enough_data_q; + phy_ready_o = !enough_data_q; + if (host_valid_o && host_ready_i) begin + if (data_o.last || (last_addr_q == byte_host_addr_d)) begin + state_d = Idle; + end else if (sent_available_data) begin + phy_ready_o = 1'b1; + state_d = phy_valid_i ? Sample : WaitData; + end + end + end + default: begin + state_d = Idle; + end + endcase + end + `FFARN(state_q, state_d, Idle, clk_i, rst_ni) + `FFARN(data_buffer_q, data_buffer_d, '0, clk_i, rst_ni) + `FFARN(byte_host_addr_q, byte_host_addr_d, '0, clk_i, rst_ni) + `FFARN(byte_phy_cnt_q, byte_phy_cnt_d, '0, clk_i, rst_ni) + `FFARN(size_q, size_d, '0, clk_i, rst_ni) + `FFARN(last_addr_q, last_addr_d, '0, clk_i, rst_ni) endmodule diff --git a/src/hyperbus_phy_if.sv b/src/hyperbus_phy_if.sv index 1b45310..37eac1e 100644 --- a/src/hyperbus_phy_if.sv +++ b/src/hyperbus_phy_if.sv @@ -5,35 +5,36 @@ // Luca Valente // Philippe Sauter +`include "common_cells/registers.svh" + module hyperbus_phy_if import hyperbus_pkg::*; #( - parameter int unsigned NumChips = 2, - parameter int unsigned NumPhys = 2, - parameter int unsigned StartupCycles = 60000, /*MHz*/ // Conservative maximum frequency estimate - parameter int unsigned SyncStages = 2, - parameter type hyper_tx_t = logic, - parameter type hyper_rx_t = logic -)( + parameter int unsigned NumChips = 2, + parameter int unsigned NumPhys = 2, + parameter int unsigned StartupCycles = 60000, + parameter int unsigned SyncStages = 2, + parameter type hyper_tx_t = logic, + parameter type hyper_rx_t = logic +) ( input logic clk_phy_i, input logic clk_phy_i_90, input logic rst_phy_ni, input logic test_mode_i, - // Config registers input phy_cfg_t cfg_i, output logic busy_o, - // Transactions + input logic trans_valid_i, output logic trans_ready_o, - input hyper_tf_t trans_i, // TODO: increase burst width! + input hyper_tf_t trans_i, input logic [NumChips-1:0] trans_cs_i, - // Transmitting channel + input logic tx_valid_i, output logic tx_ready_o, input hyper_tx_t tx_i, - // Receiving channel + output logic rx_valid_o, input logic rx_ready_i, output hyper_rx_t rx_o, - // B response + output logic b_valid_o, input logic b_ready_i, output logic b_error_o, @@ -57,80 +58,96 @@ module hyperbus_phy_if import hyperbus_pkg::*; #( assign clk_phy_90 = clk_phy_i_90; - phy_rx_t [NumPhys-1:0] phy_fifo_rx; - phy_rx_t [NumPhys-1:0] fifo_axi_rx; - logic [NumPhys-1:0] phy_fifo_valid; - logic [NumPhys-1:0] phy_fifo_ready; - logic [NumPhys-1:0] fifo_axi_valid; - logic fifo_axi_ready; - - logic [NumPhys-1:0][1:0] fifo_axi_usage; + phy_rx_t [NumPhys-1:0] phy_fifo_rx; + phy_rx_t [NumPhys-1:0] fifo_axi_rx; + logic [NumPhys-1:0] phy_fifo_valid; + logic [NumPhys-1:0] phy_fifo_ready; + logic [NumPhys-1:0] fifo_axi_valid; + logic [NumPhys-1:0][1:0] fifo_axi_usage; + logic [NumPhys-1:0] phy_tx_ready; + logic [NumPhys-1:0] phy_tx_valid; + logic [NumPhys-1:0] phy_trans_ready; + logic [NumPhys-1:0] phy_trans_valid; + logic [NumPhys-1:0] phy_b_valid; + logic [NumPhys-1:0] phy_b_error; + logic [NumPhys-1:0] phy_b_ready; - logic tx_both_ready, ts_both_ready; - logic rx_both_valid, b_both_valid; - - logic [NumPhys-1:0] phy_tx_ready; - logic phy_tx_valid; - - logic [NumPhys-1:0] phy_trans_ready; - logic [NumPhys-1:0] phy_trans_valid; - - logic [NumPhys-1:0] phy_b_valid; - logic [NumPhys-1:0] phy_b_error; - logic phy_b_ready; - - if (NumPhys==2) begin : phy_wrap + if (NumPhys == 2) begin : gen_dual_phy logic [NumPhys-1:0] phy_enable; logic [NumPhys-1:0] phy_busy; logic [NumPhys-1:0] phy_active_q, phy_active_d; + logic [NumPhys-1:0] tx_fork_valid; + logic [NumPhys-1:0] trans_fork_valid; + logic [NumPhys-1:0] rx_join_ready; logic change_phy_active; assign change_phy_active = phy_active_q != phy_enable; - assign phy_enable = cfg_i.phys_in_use ? '1 : (1 << cfg_i.which_phy); - assign phy_active_d = change_phy_active && fifo_axi_usage == '0 ? - phy_enable | phy_busy : phy_active_q; - - always_ff @(posedge clk_phy_0 or negedge rst_phy_ni ) begin - if (!rst_phy_ni) begin - phy_active_q <= '1; - end else begin - phy_active_q <= phy_active_d; - end - end - - assign busy_o = |phy_busy; - assign rx_both_valid = & (fifo_axi_valid | ~phy_active_q); - assign rx_valid_o = rx_both_valid; - assign fifo_axi_ready = rx_ready_i && rx_both_valid; - - assign rx_o.error = | ({fifo_axi_rx[1].error, fifo_axi_rx[0].error} & phy_active_q); - assign rx_o.last = & ({fifo_axi_rx[1].last, fifo_axi_rx[0].last} | ~phy_active_q); - assign tx_both_ready = & (phy_tx_ready | ~phy_active_q); - assign tx_ready_o = tx_both_ready; - assign phy_tx_valid = tx_both_ready && tx_valid_i; - - assign b_both_valid = & (phy_b_valid | ~phy_active_q); - assign b_valid_o = b_both_valid; - assign phy_b_ready = b_ready_i && b_both_valid; - assign b_error_o = | (phy_b_error & phy_active_q); - - assign ts_both_ready = change_phy_active ? '0 : - & (phy_trans_ready | ~phy_active_q); - assign trans_ready_o = ts_both_ready; - assign phy_trans_valid = change_phy_active ? '0 : - phy_trans_ready & {NumPhys{trans_valid_i}} & phy_active_q; - - for ( genvar i=0; i= $clog2(AxiDataWidth / 8)) + typedef struct packed { logic [(16*NumPhys)-1:0] data; logic last; @@ -68,85 +63,208 @@ module hyperbus_synchronous #( typedef struct packed { hyperbus_pkg::hyper_tf_t trans; logic [NumChips-1:0] cs; - } tf_t; - - logic clk_backend_90; - logic [7:0] backend_tx_clk_delay; - hyperbus_pkg::hyper_cfg_t cfg_apply; - logic cfg_apply_valid; - logic cfg_apply_ready; - - hyper_rx_t rx; - logic rx_valid; - logic rx_ready; - hyper_tx_t tx; - logic tx_valid; - logic tx_ready; - tf_t frontend_trans; - logic frontend_trans_valid; - logic frontend_trans_ready; - logic backend_trans_ready; - logic b_error; - logic b_valid; - logic b_ready; + } hyper_cmd_t; + + typedef logic [AxiAddrWidth-1:0] host_addr_t; + typedef logic [AxiDataWidth-1:0] host_data_t; + typedef logic [AxiDataWidth/8-1:0] host_strb_t; + `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) + + logic clk_backend; + logic clk_backend_90; + logic rst_backend_n; + logic [7:0] backend_tx_clk_delay; + hyperbus_pkg::phy_cfg_t frontend_cfg_apply; + logic frontend_cfg_apply_valid; + logic frontend_cfg_apply_ready; + logic frontend_cfg_apply_done; + logic frontend_drain; + logic host_idle; + hyperbus_pkg::phy_cfg_t backend_cfg_apply; + logic backend_cfg_apply_valid; + logic backend_cfg_apply_ready; + + hyperbus_pkg::frontend_cfg_t frontend_cfg; + axi_rule_t [NumChips-1:0] frontend_chip_rules; + + host_req_t host_req; + logic host_req_valid; + logic host_req_ready; + host_w_t host_w; + logic host_w_valid; + logic host_w_ready; + host_r_t host_r; + logic host_r_valid; + logic host_r_ready; + host_wrsp_t host_wrsp; + logic host_wrsp_valid; + logic host_wrsp_ready; + + hyper_rx_t midend_rx; + logic midend_rx_valid; + logic midend_rx_ready; + hyper_tx_t midend_tx; + logic midend_tx_valid; + logic midend_tx_ready; + logic midend_wrsp_error; + logic midend_wrsp_valid; + logic midend_wrsp_ready; + hyper_cmd_t midend_cmd; + logic midend_cmd_valid; + logic midend_cmd_ready; + logic midend_trans_active; + logic midend_decode_error; + + hyper_rx_t backend_rx; + logic backend_rx_valid; + logic backend_rx_ready; + hyper_tx_t backend_tx; + logic backend_tx_valid; + logic backend_tx_ready; + logic backend_wrsp_error; + logic backend_wrsp_valid; + logic backend_wrsp_ready; + hyper_cmd_t backend_cmd; + logic backend_cmd_valid; + logic backend_cmd_ready; + + assign clk_backend = clk_sys_i; + assign rst_backend_n = rst_sys_ni; + + // The synchronous top keeps the frontend/backend boundary visible without a bridge module. + assign backend_cfg_apply = frontend_cfg_apply; + assign backend_cfg_apply_valid = frontend_cfg_apply_valid; + assign frontend_cfg_apply_ready = backend_cfg_apply_ready; + assign frontend_cfg_apply_done = frontend_cfg_apply_valid && backend_cfg_apply_ready; + + assign midend_rx = backend_rx; + assign midend_rx_valid = backend_rx_valid; + assign backend_rx_ready = midend_rx_ready; + + assign backend_tx = midend_tx; + assign backend_tx_valid = midend_tx_valid; + assign midend_tx_ready = backend_tx_ready; + + assign midend_wrsp_error = backend_wrsp_error; + assign midend_wrsp_valid = backend_wrsp_valid; + assign backend_wrsp_ready = midend_wrsp_ready; + + assign backend_cmd = midend_cmd; + assign backend_cmd_valid = midend_cmd_valid; + assign midend_cmd_ready = backend_cmd_ready; hyperbus_tx_clk_delay i_tx_clk_delay ( - .rst_ni ( rst_sys_ni ), + .rst_ni ( rst_backend_n ), `ifdef TARGET_XILINX .clk_ref200_i ( clk_ref200_i ), `endif - .clk_i ( clk_sys_i ), - .in_i ( clk_sys_i ), + .clk_i ( clk_backend ), + .in_i ( clk_backend ), .delay_i ( backend_tx_clk_delay ), .out_o ( clk_backend_90 ) ); - hyperbus_frontend #( + hyperbus_cfg_frontend #( .NumChips ( NumChips ), .NumPhys ( NumPhys ), - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidth ( AxiIdWidth ), - .AxiUserWidth ( AxiUserWidth ), - .axi_req_t ( axi_req_t ), - .axi_rsp_t ( axi_rsp_t ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), - .axi_rule_t ( axi_rule_t ), - .hyper_rx_t ( hyper_rx_t ), - .hyper_tx_t ( hyper_tx_t ), - .tf_cdc_t ( tf_t ), - .RegAddrWidth ( RegAddrWidth ), - .RegDataWidth ( RegDataWidth ), - .RstChipBase ( RstChipBase ), - .RstChipSpace ( RstChipSpace ), - .RstCfg ( RstCfg ) - ) i_frontend ( - .clk_i ( clk_sys_i ), - .rst_ni ( rst_sys_ni ), - .axi_req_i ( axi_req_i ), - .axi_rsp_o ( axi_rsp_o ), - .reg_req_i ( reg_req_i ), - .reg_rsp_o ( reg_rsp_o ), - .cfg_o ( ), - .cfg_apply_o ( cfg_apply ), - .cfg_apply_valid_o ( cfg_apply_valid ), - .cfg_apply_ready_i ( cfg_apply_ready ), - .rx_i ( rx ), - .rx_valid_i ( rx_valid ), - .rx_ready_o ( rx_ready ), - .tx_o ( tx ), - .tx_valid_o ( tx_valid ), - .tx_ready_i ( tx_ready ), - .b_error_i ( b_error ), - .b_valid_i ( b_valid ), - .b_ready_o ( b_ready ), - .trans_o ( frontend_trans ), - .trans_valid_o ( frontend_trans_valid ), - .trans_ready_i ( frontend_trans_ready ) + .host_rule_t ( axi_rule_t ), + .RegDataWidth ( RegDataWidth ) + ) i_cfg_frontend ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .reg_req_i ( reg_req_i ), + .reg_rsp_o ( reg_rsp_o ), + .drain_o ( frontend_drain ), + .host_idle_i ( host_idle ), + .trans_active_i ( midend_trans_active ), + .cfg_apply_o ( frontend_cfg_apply ), + .cfg_apply_valid_o ( frontend_cfg_apply_valid ), + .cfg_apply_ready_i ( frontend_cfg_apply_ready ), + .cfg_apply_done_i ( frontend_cfg_apply_done ), + .frontend_cfg_o ( frontend_cfg ), + .chip_rules_o ( frontend_chip_rules ), + .decode_error_i ( midend_decode_error ) + ); + + hyperbus_axi_frontend #( + .AxiDataWidth ( AxiDataWidth ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .host_req_t ( host_req_t ), + .host_w_t ( host_w_t ), + .host_r_t ( host_r_t ), + .host_wrsp_t ( host_wrsp_t ) + ) i_axi_frontend ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .drain_i ( frontend_drain ), + .idle_o ( host_idle ), + .axi_req_i ( axi_req_i ), + .axi_rsp_o ( axi_rsp_o ), + .host_req_o ( host_req ), + .host_req_valid_o ( host_req_valid ), + .host_req_ready_i ( host_req_ready ), + .host_w_o ( host_w ), + .host_w_valid_o ( host_w_valid ), + .host_w_ready_i ( host_w_ready ), + .host_r_i ( host_r ), + .host_r_valid_i ( host_r_valid ), + .host_r_ready_o ( host_r_ready ), + .host_wrsp_i ( host_wrsp ), + .host_wrsp_valid_i ( host_wrsp_valid ), + .host_wrsp_ready_o ( host_wrsp_ready ) ); - assign frontend_trans_ready = backend_trans_ready; + hyperbus_midend #( + .HostAddrWidth ( AxiAddrWidth ), + .HostDataWidth ( AxiDataWidth ), + .NumChips ( NumChips ), + .NumPhys ( NumPhys ), + .host_req_t ( host_req_t ), + .host_w_t ( host_w_t ), + .host_r_t ( host_r_t ), + .host_wrsp_t ( host_wrsp_t ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .hyper_cmd_t ( hyper_cmd_t ), + .rule_t ( axi_rule_t ) + ) i_midend ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .host_req_i ( host_req ), + .host_req_valid_i ( host_req_valid ), + .host_req_ready_o ( host_req_ready ), + .host_w_i ( host_w ), + .host_w_valid_i ( host_w_valid ), + .host_w_ready_o ( host_w_ready ), + .host_r_o ( host_r ), + .host_r_valid_o ( host_r_valid ), + .host_r_ready_i ( host_r_ready ), + .host_wrsp_o ( host_wrsp ), + .host_wrsp_valid_o ( host_wrsp_valid ), + .host_wrsp_ready_i ( host_wrsp_ready ), + .frontend_cfg_i ( frontend_cfg ), + .chip_rules_i ( frontend_chip_rules ), + .trans_active_o ( midend_trans_active ), + .decode_error_o ( midend_decode_error ), + .rx_i ( midend_rx ), + .rx_valid_i ( midend_rx_valid ), + .rx_ready_o ( midend_rx_ready ), + .tx_o ( midend_tx ), + .tx_valid_o ( midend_tx_valid ), + .tx_ready_i ( midend_tx_ready ), + .wrsp_error_i ( midend_wrsp_error ), + .wrsp_valid_i ( midend_wrsp_valid ), + .wrsp_ready_o ( midend_wrsp_ready ), + .cmd_o ( midend_cmd ), + .cmd_valid_o ( midend_cmd_valid ), + .cmd_ready_i ( midend_cmd_ready ) + ); hyperbus_backend #( .NumChips ( NumChips ), @@ -155,30 +273,29 @@ module hyperbus_synchronous #( .SyncStages ( SyncStages ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), - .tf_cdc_t ( tf_t ), - .RstCfg ( RstCfg ) + .hyper_cmd_t ( hyper_cmd_t ) ) i_backend ( - .clk_i ( clk_sys_i ), + .clk_i ( clk_backend ), .clk_90_i ( clk_backend_90 ), - .rst_ni ( rst_sys_ni ), + .rst_ni ( rst_backend_n ), .test_mode_i ( test_mode_i ), - .cfg_apply_i ( cfg_apply ), - .cfg_apply_valid_i ( cfg_apply_valid ), - .cfg_apply_ready_o ( cfg_apply_ready ), + .cfg_apply_i ( backend_cfg_apply ), + .cfg_apply_valid_i ( backend_cfg_apply_valid ), + .cfg_apply_ready_o ( backend_cfg_apply_ready ), .busy_o ( ), .tx_clk_delay_o ( backend_tx_clk_delay ), - .rx_o ( rx ), - .rx_valid_o ( rx_valid ), - .rx_ready_i ( rx_ready ), - .tx_i ( tx ), - .tx_valid_i ( tx_valid ), - .tx_ready_o ( tx_ready ), - .b_error_o ( b_error ), - .b_valid_o ( b_valid ), - .b_ready_i ( b_ready ), - .trans_i ( frontend_trans ), - .trans_valid_i ( frontend_trans_valid ), - .trans_ready_o ( backend_trans_ready ), + .rx_o ( backend_rx ), + .rx_valid_o ( backend_rx_valid ), + .rx_ready_i ( backend_rx_ready ), + .tx_i ( backend_tx ), + .tx_valid_i ( backend_tx_valid ), + .tx_ready_o ( backend_tx_ready ), + .wrsp_error_o ( backend_wrsp_error ), + .wrsp_valid_o ( backend_wrsp_valid ), + .wrsp_ready_i ( backend_wrsp_ready ), + .cmd_i ( backend_cmd ), + .cmd_valid_i ( backend_cmd_valid ), + .cmd_ready_o ( backend_cmd_ready ), .hyper_cs_no ( hyper_cs_no ), .hyper_ck_o ( hyper_ck_o ), .hyper_ck_no ( hyper_ck_no ), diff --git a/src/hyperbus_trx.sv b/src/hyperbus_trx.sv index cc4c79b..7ca194c 100644 --- a/src/hyperbus_trx.sv +++ b/src/hyperbus_trx.sv @@ -59,7 +59,8 @@ module hyperbus_trx #( logic rx_rwds_clk_ena; logic rx_rwds_clk_orig; logic rx_rwds_clk; - logic rx_rwds_soft_rst; + logic rx_rwds_clk_n; + logic rx_capture_rst; logic [15:0] rx_rwds_fifo_in; logic rx_rwds_fifo_valid; logic rx_rwds_fifo_ready; @@ -86,7 +87,7 @@ module hyperbus_trx #( // Synchronize output chip select to shifted differential output clock always_ff @(negedge clk_i or negedge rst_ni) begin : proc_ff_tx_shift90 - if (~rst_ni) hyper_cs_no <= '1; + if (!rst_ni) hyper_cs_no <= '1; else hyper_cs_no <= cs_ena_i ? ~cs_i : '1; end @@ -117,7 +118,7 @@ module hyperbus_trx #( // Delay output, clock enables to be synchronous with DDR-converted data // The delayed clock also ensures t_CSS is respected at the start, end of CS always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_tx_delay - if(~rst_ni) begin + if (!rst_ni) begin hyper_rwds_oe_o <= 1'b0; hyper_dq_oe_o <= 1'b0; tx_clk_ena_q <= 1'b0; @@ -134,13 +135,13 @@ module hyperbus_trx #( // Sample RWDS for extra latency determination. always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_rwds_sample - if (~rst_ni) rwds_sample_o <= '0; + if (!rst_ni) rwds_sample_o <= '0; else if (rwds_sample_ena_i) rwds_sample_o <= hyper_rwds_i; end // Set and Reset RX clock enable always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_rx_delay - if (~rst_ni) rx_rwds_clk_ena <= 1'b0; + if (!rst_ni) rx_rwds_clk_ena <= 1'b0; else if (rx_clk_set_i) rx_rwds_clk_ena <= 1'b1; else if (rx_clk_reset_i) rx_rwds_clk_ena <= 1'b0; end @@ -148,7 +149,7 @@ module hyperbus_trx #( // Shift RWDS clock by 90 degrees `ifdef TARGET_XILINX hyperbus_rwds_delay i_delay_rx_rwds_90 ( - .rst_i ( ~rst_ni ), + .rst_i ( !rst_ni ), .clk_i, .in_i ( hyper_rwds_i ), .delay_i ( rx_clk_delay_i ), @@ -174,12 +175,12 @@ module hyperbus_trx #( // Reset RX state on async reset or on gated clock (whenever inactive) // TODO: is this safe? Replace with tech cells? - assign rx_rwds_soft_rst = ~rst_ni | (~rx_rwds_clk_ena & ~test_mode_i); + assign rx_capture_rst = !rst_ni || (!rx_rwds_clk_ena && !test_mode_i); // RX data is valid one cycle after each RX soft reset - always_ff @(posedge rx_rwds_clk or posedge rx_rwds_soft_rst) begin : proc_read_in_valid - if (rx_rwds_soft_rst) rx_rwds_fifo_valid <= 1'b0; - else rx_rwds_fifo_valid <= 1'b1; + always_ff @(posedge rx_rwds_clk or posedge rx_capture_rst) begin : proc_read_in_valid + if (rx_capture_rst) rx_rwds_fifo_valid <= 1'b0; + else rx_rwds_fifo_valid <= 1'b1; end // If testing, replace gated RWDS clock with primary (PHY) clock; @@ -197,9 +198,9 @@ module hyperbus_trx #( // Data input DDR conversion assign rx_rwds_fifo_in[7:0] = hyper_dq_i; - always @(posedge rx_rwds_clk or posedge rx_rwds_soft_rst) begin : proc_ff_ddr_in - if(rx_rwds_soft_rst) rx_rwds_fifo_in[15:8] <= '0; - else rx_rwds_fifo_in[15:8] <= hyper_dq_i; + always @(posedge rx_rwds_clk or posedge rx_capture_rst) begin : proc_ff_ddr_in + if (rx_capture_rst) rx_rwds_fifo_in[15:8] <= '0; + else rx_rwds_fifo_in[15:8] <= hyper_dq_i; end tc_clk_inverter i_rwds_clk_inverter ( @@ -231,6 +232,10 @@ module hyperbus_trx #( `ifndef SYNTHESIS always @(negedge rx_rwds_fifo_ready) assert(rx_rwds_fifo_ready) else $error("%m: HyperBus RX FIFO must always be ready to receive data"); + + rx_capture_control_exclusive : assert property ( + @(posedge clk_i) disable iff (!rst_ni) !(rx_clk_set_i && rx_clk_reset_i) + ) else $error("%m: RWDS capture set and reset asserted together"); `endif endmodule diff --git a/src/hyperbus_w2phy.sv b/src/hyperbus_w2phy.sv index 78a04fa..681dfd0 100644 --- a/src/hyperbus_w2phy.sv +++ b/src/hyperbus_w2phy.sv @@ -6,209 +6,186 @@ // Paul Scheffler // Luca Valente +`include "common_cells/registers.svh" + module hyperbus_w2phy #( - parameter int unsigned AxiDataWidth = -1, - parameter int unsigned NumPhys = -1, - parameter int unsigned BurstLength = -1, - parameter type T = logic, - parameter int unsigned AddrWidth = $clog2(AxiDataWidth/8) + parameter int unsigned HostDataWidth = -1, + parameter int unsigned NumPhys = -1, + parameter type T = logic, + parameter int unsigned AddrWidth = $clog2(HostDataWidth / 8) ) ( - input logic clk_i, - input logic rst_ni, - input logic [2:0] size, - input logic [AddrWidth-1:0] start_addr, - input logic [BurstLength-1:0] len, - input logic is_a_write, - input logic trans_handshake, - input logic axi_valid_i, - output logic axi_ready_o, - input T data_i, - output logic phy_valid_o, - input logic phy_ready_i, - output logic [16*NumPhys-1:0] data_o, - output logic last_o, - output logic [2*NumPhys-1:0] strb_o + input logic clk_i, + input logic rst_ni, + input logic [2:0] size_i, + input logic [AddrWidth-1:0] start_addr_i, + input logic is_write_i, + input logic cmd_fire_i, + input logic host_valid_i, + output logic host_ready_o, + input T data_i, + output logic phy_valid_o, + input logic phy_ready_i, + output logic [16*NumPhys-1:0] data_o, + output logic last_o, + output logic [2*NumPhys-1:0] strb_o ); - localparam int unsigned NumAxiBytes = AxiDataWidth/8; - localparam int unsigned NumPhyBytes = NumPhys*2; - localparam int unsigned AxiBytesInPhyBeat = NumAxiBytes/NumPhyBytes; - localparam int unsigned WordCntWidth = (AxiBytesInPhyBeat==1) ? 1 : $clog2(AxiBytesInPhyBeat); - // Cutting the combinatorial path between AXI master and cdc fifo - typedef enum logic [2:0] { - Idle, - Sample, - CntReady - } hyper_upsizer_state_t; - - hyper_upsizer_state_t state_d, state_q; - - typedef struct packed { - logic [AxiDataWidth/8-1:0] strb; - logic [AxiDataWidth-1:0] data; - logic last; - } w2phy_chan_t; - - w2phy_chan_t data_buffer_d, data_buffer_q; - - logic is_16_bw, is_8_bw; - logic upsize; - logic enough_data; - logic first_tx_d, first_tx_q; - - - logic [NumPhys*2-1:0] mask_strobe_d, mask_strobe_q; - logic [WordCntWidth-1:0] word_cnt; - logic [AddrWidth-1:0] byte_idx_d, byte_idx_q; - logic [3:0] size_d, size_q; - logic [AddrWidth-1:0] cnt_data_phy_d, cnt_data_phy_q; - logic keep_sampling, keep_sending; - logic upsize_q; - - assign is_8_bw = (size_d == 0); - assign is_16_bw = (size_d == 1) ; - assign upsize = (is_16_bw && (NumPhys==2)) | is_8_bw ; - assign upsize_q = ( (size_q==1) && (NumPhys==2)) | (size_q==0) ; - assign enough_data = !upsize; - assign keep_sampling = (size_d<($clog2(NumPhys)+1)) && (byte_idx_d[NumPhys-1:0]!='0); - assign keep_sending = (size_d>($clog2(NumPhys)+1)) && (cnt_data_phy_d != byte_idx_q); - assign word_cnt = cnt_data_phy_q>>($clog2(NumPhys)+1); - - - assign data_o = data_buffer_q.data[(16*NumPhys)*word_cnt +:(16*NumPhys)]; - assign strb_o = data_buffer_q.strb[ (2*NumPhys)*word_cnt +: (2*NumPhys)] & mask_strobe_q; - assign last_o = data_buffer_q.last && (!keep_sending || upsize_q); - - always_comb begin : counter - byte_idx_d = byte_idx_q; - size_d = size_q; - cnt_data_phy_d = cnt_data_phy_q; - first_tx_d = first_tx_q; - if (trans_handshake & is_a_write) begin - byte_idx_d = start_addr; - size_d = size; - cnt_data_phy_d = (start_addr>>NumPhys)<>size_d)<< size_d) + (1<> size_d) << size_d) + (1 << size_d); + first_tx_d = 1'b0; end - CntReady: begin - axi_ready_o = 1'b0; - phy_valid_o = 1'b1; - if(phy_ready_i) begin - if(last_o) begin - if(trans_handshake) begin - state_d = Sample; - end else begin - state_d = Idle; - end - end else if (size_d>=NumPhys) begin - if (cnt_data_phy_d != byte_idx_q) begin - state_d = CntReady; - end else if (axi_valid_i) begin - axi_ready_o = 1'b1; - state_d = enough_data ? CntReady : Sample; - end else begin - state_d = Sample; - end - end else if (size_d= NumPhys) begin + if (cnt_data_phy_d != byte_idx_q) begin + state_d = CntReady; + end else if (host_valid_i) begin + host_ready_o = 1'b1; + state_d = enough_data ? CntReady : Sample; + end else begin + state_d = Sample; + end + end else if (cnt_data_phy_d[NumPhys-1:0] == '0) begin + host_ready_o = !upsize; + state_d = Sample; + end + end + end + default: begin + state_d = Idle; + end + endcase + end + + `FFARN(state_q, state_d, Idle, clk_i, rst_ni) + `FFARN(data_buffer_q, data_buffer_d, '0, clk_i, rst_ni) + `FFARN(byte_idx_q, byte_idx_d, '0, clk_i, rst_ni) + `FFARN(size_q, size_d, '0, clk_i, rst_ni) + `FFARN(cnt_data_phy_q, cnt_data_phy_d, '0, clk_i, rst_ni) + `FFARN(first_tx_q, first_tx_d, 1'b0, clk_i, rst_ni) + `FFARN(mask_strobe_q, mask_strobe_d, '0, clk_i, rst_ni) endmodule diff --git a/src/regs/hyperbus_cfg_regblock.sv b/src/regs/hyperbus_cfg_regblock.sv index 75b0286..dd21d44 100644 --- a/src/regs/hyperbus_cfg_regblock.sv +++ b/src/regs/hyperbus_cfg_regblock.sv @@ -16,6 +16,7 @@ module hyperbus_cfg_regblock ( output logic [31:0] s_apb_prdata, output logic s_apb_pslverr, + input hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__in_t hwif_in, output hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t hwif_out ); @@ -94,8 +95,7 @@ module hyperbus_cfg_regblock ( logic t_tx_clk_delay; logic address_mask_msb; logic address_space; - logic phys_in_use; - logic which_phy; + logic dual_phy; logic t_csh_cycles; logic csn_to_ck_cycles; logic chip0_base; @@ -106,6 +106,8 @@ module hyperbus_cfg_regblock ( logic chip2_bound; logic chip3_base; logic chip3_bound; + logic flush; + logic status; logic chip4_base; logic chip4_bound; logic chip5_base; @@ -136,8 +138,7 @@ module hyperbus_cfg_regblock ( decoded_reg_strb.t_tx_clk_delay = cpuif_req_masked & (cpuif_addr == 7'h14); decoded_reg_strb.address_mask_msb = cpuif_req_masked & (cpuif_addr == 7'h18); decoded_reg_strb.address_space = cpuif_req_masked & (cpuif_addr == 7'h1c); - decoded_reg_strb.phys_in_use = cpuif_req_masked & (cpuif_addr == 7'h20); - decoded_reg_strb.which_phy = cpuif_req_masked & (cpuif_addr == 7'h24); + decoded_reg_strb.dual_phy = cpuif_req_masked & (cpuif_addr == 7'h20); decoded_reg_strb.t_csh_cycles = cpuif_req_masked & (cpuif_addr == 7'h28); decoded_reg_strb.csn_to_ck_cycles = cpuif_req_masked & (cpuif_addr == 7'h2c); decoded_reg_strb.chip0_base = cpuif_req_masked & (cpuif_addr == 7'h30); @@ -148,6 +149,8 @@ module hyperbus_cfg_regblock ( decoded_reg_strb.chip2_bound = cpuif_req_masked & (cpuif_addr == 7'h44); decoded_reg_strb.chip3_base = cpuif_req_masked & (cpuif_addr == 7'h48); decoded_reg_strb.chip3_bound = cpuif_req_masked & (cpuif_addr == 7'h4c); + decoded_reg_strb.flush = cpuif_req_masked & (cpuif_addr == 7'h50) & cpuif_req_is_wr; + decoded_reg_strb.status = cpuif_req_masked & (cpuif_addr == 7'h54); decoded_reg_strb.chip4_base = cpuif_req_masked & (cpuif_addr == 7'h58); decoded_reg_strb.chip4_bound = cpuif_req_masked & (cpuif_addr == 7'h5c); decoded_reg_strb.chip5_base = cpuif_req_masked & (cpuif_addr == 7'h60); @@ -231,13 +234,7 @@ module hyperbus_cfg_regblock ( logic next; logic load_next; } value; - } phys_in_use; - struct { - struct { - logic next; - logic load_next; - } value; - } which_phy; + } dual_phy; struct { struct { logic [3:0] next; @@ -298,6 +295,18 @@ module hyperbus_cfg_regblock ( logic load_next; } value; } chip3_bound; + struct { + struct { + logic next; + logic load_next; + } request; + } flush; + struct { + struct { + logic next; + logic load_next; + } decode_error; + } status; struct { struct { logic [9:0] next; @@ -400,12 +409,7 @@ module hyperbus_cfg_regblock ( struct { logic value; } value; - } phys_in_use; - struct { - struct { - logic value; - } value; - } which_phy; + } dual_phy; struct { struct { logic [3:0] value; @@ -456,6 +460,16 @@ module hyperbus_cfg_regblock ( logic [9:0] value; } value; } chip3_bound; + struct { + struct { + logic value; + } request; + } flush; + struct { + struct { + logic value; + } decode_error; + } status; struct { struct { logic [9:0] value; @@ -729,52 +743,29 @@ module hyperbus_cfg_regblock ( end end assign hwif_out.address_space.value.value = field_storage.address_space.value.value; - // Field: hyperbus_cfg_regs.phys_in_use.value + // Field: hyperbus_cfg_regs.dual_phy.value always_comb begin automatic logic [0:0] next_c; automatic logic load_next_c; - next_c = field_storage.phys_in_use.value.value; + next_c = field_storage.dual_phy.value.value; load_next_c = '0; - if(decoded_reg_strb.phys_in_use && decoded_req_is_wr) begin // SW write - next_c = (field_storage.phys_in_use.value.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]); + if(decoded_reg_strb.dual_phy && decoded_req_is_wr) begin // SW write + next_c = (field_storage.dual_phy.value.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]); load_next_c = '1; end - field_combo.phys_in_use.value.next = next_c; - field_combo.phys_in_use.value.load_next = load_next_c; + field_combo.dual_phy.value.next = next_c; + field_combo.dual_phy.value.load_next = load_next_c; end always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin - field_storage.phys_in_use.value.value <= 1'h1; + field_storage.dual_phy.value.value <= 1'h1; end else begin - if(field_combo.phys_in_use.value.load_next) begin - field_storage.phys_in_use.value.value <= field_combo.phys_in_use.value.next; + if(field_combo.dual_phy.value.load_next) begin + field_storage.dual_phy.value.value <= field_combo.dual_phy.value.next; end end end - assign hwif_out.phys_in_use.value.value = field_storage.phys_in_use.value.value; - // Field: hyperbus_cfg_regs.which_phy.value - always_comb begin - automatic logic [0:0] next_c; - automatic logic load_next_c; - next_c = field_storage.which_phy.value.value; - load_next_c = '0; - if(decoded_reg_strb.which_phy && decoded_req_is_wr) begin // SW write - next_c = (field_storage.which_phy.value.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]); - load_next_c = '1; - end - field_combo.which_phy.value.next = next_c; - field_combo.which_phy.value.load_next = load_next_c; - end - always_ff @(posedge clk or negedge arst_n) begin - if(~arst_n) begin - field_storage.which_phy.value.value <= 1'h1; - end else begin - if(field_combo.which_phy.value.load_next) begin - field_storage.which_phy.value.value <= field_combo.which_phy.value.next; - end - end - end - assign hwif_out.which_phy.value.value = field_storage.which_phy.value.value; + assign hwif_out.dual_phy.value.value = field_storage.dual_phy.value.value; // Field: hyperbus_cfg_regs.t_csh_cycles.value always_comb begin automatic logic [3:0] next_c; @@ -1005,6 +996,51 @@ module hyperbus_cfg_regblock ( end end assign hwif_out.chip3_bound.value.value = field_storage.chip3_bound.value.value; + // Field: hyperbus_cfg_regs.flush.request + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.flush.request.value; + load_next_c = '0; + if(decoded_reg_strb.flush && decoded_req_is_wr) begin // SW write + next_c = (field_storage.flush.request.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]); + load_next_c = '1; + end + field_combo.flush.request.next = next_c; + field_combo.flush.request.load_next = load_next_c; + end + always_ff @(posedge clk) begin + if(field_combo.flush.request.load_next) begin + field_storage.flush.request.value <= field_combo.flush.request.next; + end + end + assign hwif_out.flush.request.value = field_storage.flush.request.value; + // Field: hyperbus_cfg_regs.status.decode_error + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.status.decode_error.value; + load_next_c = '0; + if(hwif_in.status.decode_error.hwset) begin // HW Set + next_c = '1; + load_next_c = '1; + end else if(decoded_reg_strb.status && decoded_req_is_wr) begin // SW write 1 clear + next_c = field_storage.status.decode_error.value & ~(decoded_wr_data[0:0] & decoded_wr_biten[0:0]); + load_next_c = '1; + end + field_combo.status.decode_error.next = next_c; + field_combo.status.decode_error.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.status.decode_error.value <= 1'h0; + end else begin + if(field_combo.status.decode_error.load_next) begin + field_storage.status.decode_error.value <= field_combo.status.decode_error.next; + end + end + end + assign hwif_out.status.decode_error.value = field_storage.status.decode_error.value; // Field: hyperbus_cfg_regs.chip4_base.value always_comb begin automatic logic [9:0] next_c; @@ -1237,10 +1273,7 @@ module hyperbus_cfg_regblock ( readback_data_var[0] = field_storage.address_space.value.value; end if(rd_mux_addr == 7'h20) begin - readback_data_var[0] = field_storage.phys_in_use.value.value; - end - if(rd_mux_addr == 7'h24) begin - readback_data_var[0] = field_storage.which_phy.value.value; + readback_data_var[0] = field_storage.dual_phy.value.value; end if(rd_mux_addr == 7'h28) begin readback_data_var[3:0] = field_storage.t_csh_cycles.value.value; @@ -1272,6 +1305,9 @@ module hyperbus_cfg_regblock ( if(rd_mux_addr == 7'h4c) begin readback_data_var[31:22] = field_storage.chip3_bound.value.value; end + if(rd_mux_addr == 7'h54) begin + readback_data_var[0] = field_storage.status.decode_error.value; + end if(rd_mux_addr == 7'h58) begin readback_data_var[31:22] = field_storage.chip4_base.value.value; end diff --git a/src/regs/hyperbus_cfg_regblock_pkg.sv b/src/regs/hyperbus_cfg_regblock_pkg.sv index 85c5edb..7d61e63 100644 --- a/src/regs/hyperbus_cfg_regblock_pkg.sv +++ b/src/regs/hyperbus_cfg_regblock_pkg.sv @@ -7,6 +7,18 @@ package hyperbus_cfg_regblock_pkg; localparam HYPERBUS_CFG_REGBLOCK_MIN_ADDR_WIDTH = 7; localparam HYPERBUS_CFG_REGBLOCK_SIZE = 'h78; + typedef struct { + logic hwset; + } hyperbus_cfg_regs__status__decode_error__in_t; + + typedef struct { + hyperbus_cfg_regs__status__decode_error__in_t decode_error; + } hyperbus_cfg_regs__status__in_t; + + typedef struct { + hyperbus_cfg_regs__status__in_t status; + } hyperbus_cfg_regs__in_t; + typedef struct { logic [3:0] value; } hyperbus_cfg_regs__t_latency_access__value__out_t; @@ -83,19 +95,11 @@ package hyperbus_cfg_regblock_pkg; typedef struct { logic value; - } hyperbus_cfg_regs__phys_in_use__value__out_t; - - typedef struct { - hyperbus_cfg_regs__phys_in_use__value__out_t value; - } hyperbus_cfg_regs__phys_in_use__out_t; - - typedef struct { - logic value; - } hyperbus_cfg_regs__which_phy__value__out_t; + } hyperbus_cfg_regs__dual_phy__value__out_t; typedef struct { - hyperbus_cfg_regs__which_phy__value__out_t value; - } hyperbus_cfg_regs__which_phy__out_t; + hyperbus_cfg_regs__dual_phy__value__out_t value; + } hyperbus_cfg_regs__dual_phy__out_t; typedef struct { logic [3:0] value; @@ -177,6 +181,22 @@ package hyperbus_cfg_regblock_pkg; hyperbus_cfg_regs__chip3_bound__value__out_t value; } hyperbus_cfg_regs__chip3_bound__out_t; + typedef struct { + logic value; + } hyperbus_cfg_regs__flush__request__out_t; + + typedef struct { + hyperbus_cfg_regs__flush__request__out_t request; + } hyperbus_cfg_regs__flush__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__status__decode_error__out_t; + + typedef struct { + hyperbus_cfg_regs__status__decode_error__out_t decode_error; + } hyperbus_cfg_regs__status__out_t; + typedef struct { logic [9:0] value; } hyperbus_cfg_regs__chip4_base__value__out_t; @@ -250,8 +270,7 @@ package hyperbus_cfg_regblock_pkg; hyperbus_cfg_regs__t_tx_clk_delay__out_t t_tx_clk_delay; hyperbus_cfg_regs__address_mask_msb__out_t address_mask_msb; hyperbus_cfg_regs__address_space__out_t address_space; - hyperbus_cfg_regs__phys_in_use__out_t phys_in_use; - hyperbus_cfg_regs__which_phy__out_t which_phy; + hyperbus_cfg_regs__dual_phy__out_t dual_phy; hyperbus_cfg_regs__t_csh_cycles__out_t t_csh_cycles; hyperbus_cfg_regs__csn_to_ck_cycles__out_t csn_to_ck_cycles; hyperbus_cfg_regs__chip0_base__out_t chip0_base; @@ -262,6 +281,8 @@ package hyperbus_cfg_regblock_pkg; hyperbus_cfg_regs__chip2_bound__out_t chip2_bound; hyperbus_cfg_regs__chip3_base__out_t chip3_base; hyperbus_cfg_regs__chip3_bound__out_t chip3_bound; + hyperbus_cfg_regs__flush__out_t flush; + hyperbus_cfg_regs__status__out_t status; hyperbus_cfg_regs__chip4_base__out_t chip4_base; hyperbus_cfg_regs__chip4_bound__out_t chip4_bound; hyperbus_cfg_regs__chip5_base__out_t chip5_base; diff --git a/src/regs/hyperbus_cfg_regs.rdl b/src/regs/hyperbus_cfg_regs.rdl index 515e294..7cd9d79 100644 --- a/src/regs/hyperbus_cfg_regs.rdl +++ b/src/regs/hyperbus_cfg_regs.rdl @@ -63,13 +63,8 @@ addrmap hyperbus_cfg_regs { field { sw = rw; hw = r; reset = 1'b0; } value; }; - reg phys_in_use { - desc = "Select whether one or both PHY lanes are active."; - field { sw = rw; hw = r; reset = 1'b1; } value; - }; - - reg which_phy { - desc = "Select the PHY lane used when only one PHY is active."; + reg dual_phy { + desc = "Enable both PHY lanes; when clear, only PHY lane 0 is active."; field { sw = rw; hw = r; reset = 1'b1; } value; }; @@ -163,6 +158,28 @@ addrmap hyperbus_cfg_regs { field { sw = rw; hw = r; reset = 10'd8; } value[31:22]; }; + reg flush { + desc = "Write to stop accepting requests and drain all in-flight HyperBus operations."; + field { + desc = "Writing any value executes the ordering barrier; the value is not stored."; + sw = w; + hw = r; + } request; + }; + + reg status { + desc = "Sticky protocol and address error status."; + field { + desc = "A host request did not match any configured chip address range."; + sw = rw; + hw = r; + woclr; + hwset; + precedence = hw; + reset = 1'b0; + } decode_error; + }; + t_latency_access t_latency_access @ 0x00; en_latency_additional en_latency_additional @ 0x04; t_burst_max t_burst_max @ 0x08; @@ -171,8 +188,7 @@ addrmap hyperbus_cfg_regs { t_tx_clk_delay t_tx_clk_delay @ 0x14; address_mask_msb address_mask_msb @ 0x18; address_space address_space @ 0x1c; - phys_in_use phys_in_use @ 0x20; - which_phy which_phy @ 0x24; + dual_phy dual_phy @ 0x20; t_csh_cycles t_csh_cycles @ 0x28; csn_to_ck_cycles csn_to_ck_cycles @ 0x2c; chip0_base chip0_base @ 0x30; @@ -183,6 +199,8 @@ addrmap hyperbus_cfg_regs { chip2_bound chip2_bound @ 0x44; chip3_base chip3_base @ 0x48; chip3_bound chip3_bound @ 0x4c; + flush flush @ 0x50; + status status @ 0x54; chip4_base chip4_base @ 0x58; chip4_bound chip4_bound @ 0x5c; chip5_base chip5_base @ 0x60; diff --git a/test/axi_hyper_tb.sv b/test/axi_hyper_tb.sv index fc5a1e5..b1e7904 100644 --- a/test/axi_hyper_tb.sv +++ b/test/axi_hyper_tb.sv @@ -41,6 +41,8 @@ module axi_hyper_tb parameter int unsigned TbSlowGapCycles = 64, /// Temporary t_burst_max used to force HyperBus segment restarts in the slow stress phase. parameter int unsigned TbSlowBurstMax = 16, + /// Maximum system-clock cycles before a stalled regression is terminated. + parameter longint unsigned TbTimeoutCycles = 5_000_000, /// Annotate the HyperRAM timing SDF. Disable for fast RTL regressions. parameter bit TbAnnotateSdf = 1'b1 ); @@ -77,6 +79,22 @@ module axi_hyper_tb logic end_of_sim; logic [31:0] segment_start_count; + logic [63:0] cycle_count; + + sim_timeout #( + .Cycles ( TbTimeoutCycles ) + ) i_sim_timeout ( + .clk_i ( clk ), + .rst_ni ( rst_n ) + ); + + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + cycle_count <= '0; + end else begin + cycle_count <= cycle_count + 1'b1; + end + end /////////////////////// @@ -380,6 +398,44 @@ module axi_hyper_tb end endtask + task automatic check_unaligned_word_access(input axi_ctrl_master_t axi_drv); + localparam axi_addr_t BaseAddr = axi_addr_t'(32'h8000_0180); + localparam logic [63:0] InitialData = 64'h8877_6655_4433_2211; + localparam logic [23:0] UpdatedData = 24'hc3_b2_a1; + axi_ctrl_master_t::ax_beat_t ax = new(); + axi_ctrl_master_t::w_beat_t w = new(); + axi_ctrl_master_t::b_beat_t b; + axi_ctrl_master_t::r_beat_t r; + + axi_write_subword(axi_drv, BaseAddr, InitialData, 3); + + ax.ax_addr = BaseAddr + 1; + ax.ax_id = '0; + ax.ax_len = '0; + ax.ax_size = 2; + ax.ax_burst = axi_pkg::BURST_INCR; + axi_drv.send_aw(ax); + + w.w_data = 64'(UpdatedData) << 8; + w.w_strb = 8'b0000_1110; + w.w_last = 1'b1; + axi_drv.send_w(w); + axi_drv.recv_b(b); + if (b.b_resp != axi_pkg::RESP_OKAY) begin + $error("[AXI-UNALIGNED] Write returned response %0d", b.b_resp); + end + + axi_check_subword(axi_drv, BaseAddr, 64'h8877_6655_c3b2_a111, 3); + + axi_drv.send_ar(ax); + axi_drv.recv_r(r); + if ((r.r_resp != axi_pkg::RESP_OKAY) || !r.r_last || + (r.r_data[31:8] != UpdatedData)) begin + $error("[AXI-UNALIGNED] Read returned data=0x%016x last=%0b resp=%0d", + r.r_data, r.r_last, r.r_resp); + end + endtask + task automatic axi_write_slow( input axi_ctrl_master_t axi_drv, input axi_addr_t addr, @@ -449,6 +505,114 @@ module axi_hyper_tb end endtask + task automatic run_performance_smoke(input axi_ctrl_master_t axi_drv); + localparam int unsigned NumCases = 5; + localparam axi_addr_t PerfBaseAddr = axi_addr_t'(32'h8000_8000); + int unsigned burst_beats [NumCases] = '{1, 4, 16, 64, 256}; + int unsigned write_baseline [NumCases]; + int unsigned read_baseline [NumCases]; + int unsigned segment_limit [NumCases]; + logic [63:0] write_cycles [NumCases]; + logic [63:0] read_cycles [NumCases]; + logic [63:0] cycle_snapshot; + logic [31:0] segment_snapshot; + logic [31:0] write_segments; + logic [31:0] read_segments; + axi_addr_t burst_addr; + int unsigned write_cycle_limit; + int unsigned read_cycle_limit; + real backend_cycles_per_system_cycle; + real write_bits_per_phy_cycle; + real read_bits_per_phy_cycle; + + backend_cycles_per_system_cycle = 1.0; + if (TbDutVariant == 0) begin + backend_cycles_per_system_cycle = 0.5; + end else if (TbDutVariant == 2) begin + backend_cycles_per_system_cycle = real'(TbCyclTime) / real'(TbPhyCyclTime); + end + + // Default-configuration baselines in system-clock cycles for each supported top. + write_baseline = '{44, 55, 103, 295, 1103}; + read_baseline = '{59, 71, 119, 311, 1119}; + segment_limit = '{1, 1, 1, 1, 2}; + if (TbDutVariant == 1) begin + write_baseline = '{22, 28, 52, 148, 552}; + read_baseline = '{29, 35, 59, 155, 559}; + end else if (TbDutVariant == 2) begin + write_baseline = '{36, 42, 71, 186, 671}; + read_baseline = '{43, 49, 78, 193, 678}; + end + if (NumPhys == 1) begin + write_baseline = '{24, 36, 84, 276, 1084}; + read_baseline = '{30, 42, 90, 282, 1090}; + segment_limit = '{1, 1, 1, 1, 3}; + end + + $display("==========================="); + $display("= AXI performance smoke ="); + $display("==========================="); + + for (int unsigned test_idx = 0; test_idx < NumCases; test_idx++) begin + burst_addr = PerfBaseAddr + axi_addr_t'(test_idx * 32'h1000); + + cycle_snapshot = cycle_count; + segment_snapshot = segment_start_count; + axi_write_slow(axi_drv, burst_addr, burst_beats[test_idx], 0); + write_cycles[test_idx] = cycle_count - cycle_snapshot; + write_segments = segment_start_count - segment_snapshot; + + cycle_snapshot = cycle_count; + segment_snapshot = segment_start_count; + axi_read_slow_check(axi_drv, burst_addr, burst_beats[test_idx], 0); + read_cycles[test_idx] = cycle_count - cycle_snapshot; + read_segments = segment_start_count - segment_snapshot; + + $display("[PERF] variant=%0d phys=%0d beats=%0d write_cycles=%0d read_cycles=%0d write_segments=%0d read_segments=%0d", + TbDutVariant, NumPhys, burst_beats[test_idx], write_cycles[test_idx], + read_cycles[test_idx], write_segments, read_segments); + + if (burst_beats[test_idx] == 256) begin + write_bits_per_phy_cycle = real'(burst_beats[test_idx] * TbAxiDataWidthFull) / + real'(NumPhys * write_cycles[test_idx]) / backend_cycles_per_system_cycle; + read_bits_per_phy_cycle = real'(burst_beats[test_idx] * TbAxiDataWidthFull) / + real'(NumPhys * read_cycles[test_idx]) / backend_cycles_per_system_cycle; + $display("[PERF-MAX] variant=%0d write_bits_per_phy_cycle=%0.3f read_bits_per_phy_cycle=%0.3f", + TbDutVariant, write_bits_per_phy_cycle, read_bits_per_phy_cycle); + end + + write_cycle_limit = write_baseline[test_idx] + write_baseline[test_idx] / 5 + 2; + read_cycle_limit = read_baseline[test_idx] + read_baseline[test_idx] / 5 + 2; + if (write_cycles[test_idx] > write_cycle_limit) begin + $error("[PERF] %0d-beat write took %0d cycles, limit is %0d", + burst_beats[test_idx], write_cycles[test_idx], write_cycle_limit); + end + if (read_cycles[test_idx] > read_cycle_limit) begin + $error("[PERF] %0d-beat read took %0d cycles, limit is %0d", + burst_beats[test_idx], read_cycles[test_idx], read_cycle_limit); + end + if ((write_segments != segment_limit[test_idx]) || + (read_segments != segment_limit[test_idx])) begin + $error("[PERF] %0d-beat burst used unexpected segments: write=%0d read=%0d limit=%0d", + burst_beats[test_idx], write_segments, read_segments, + segment_limit[test_idx]); + end + end + + for (int unsigned test_idx = 1; test_idx < NumCases; test_idx++) begin + if ((write_cycles[test_idx] * burst_beats[test_idx-1]) > + (write_cycles[test_idx-1] * burst_beats[test_idx])) begin + $error("[PERF] Write cycles per beat did not improve from %0d to %0d beats", + burst_beats[test_idx-1], burst_beats[test_idx]); + end + if ((read_cycles[test_idx] * burst_beats[test_idx-1]) > + (read_cycles[test_idx-1] * burst_beats[test_idx])) begin + $error("[PERF] Read cycles per beat did not improve from %0d to %0d beats", + burst_beats[test_idx-1], burst_beats[test_idx]); + end + end + endtask + task automatic run_slow_backpressure_test( input axi_ctrl_master_t axi_drv, input reg_bus_master_t reg_drv @@ -496,6 +660,354 @@ module axi_hyper_tb if (reg_error != 1'b0) $error("unexpected error"); endtask + task automatic check_config_barrier( + input axi_ctrl_master_t axi_drv, + input reg_bus_master_t reg_drv + ); + localparam axi_addr_t BarrierAddr = axi_addr_t'(32'h8000_6000); + logic reg_error; + time write_done_time; + time flush_done_time; + + write_done_time = 0; + flush_done_time = 0; + $display("==========================="); + $display("= Config flush barrier ="); + $display("==========================="); + + fork + begin + axi_write_slow(axi_drv, BarrierAddr, 16, 16); + write_done_time = $time; + end + begin + repeat (32) @(posedge clk); + reg_drv.send_write(32'h50, '0, '1, reg_error); + flush_done_time = $time; + if (reg_error != 1'b0) begin + $error("[CFG-BARRIER] Flush register write returned an error"); + end + end + join + + if ((write_done_time == 0) || (flush_done_time < write_done_time)) begin + $error("[CFG-BARRIER] Flush completed at %0t before AXI write completed at %0t", + flush_done_time, write_done_time); + end + endtask + + task automatic check_decode_errors( + input axi_ctrl_master_t axi_drv, + input reg_bus_master_t reg_drv + ); + localparam axi_addr_t InvalidAddr = axi_addr_t'(32'h9000_0000); + axi_ctrl_master_t::ax_beat_t ax = new(); + axi_ctrl_master_t::w_beat_t w = new(); + axi_ctrl_master_t::b_beat_t b; + axi_ctrl_master_t::r_beat_t r; + logic [31:0] status; + logic reg_error; + + $display("==========================="); + $display("= Decode error handling ="); + $display("==========================="); + + ax.ax_addr = InvalidAddr; + ax.ax_id = '0; + ax.ax_len = 1; + ax.ax_size = 3; + ax.ax_burst = axi_pkg::BURST_INCR; + axi_drv.send_ar(ax); + for (int unsigned beat = 0; beat < 2; beat++) begin + axi_drv.recv_r(r); + if ((r.r_resp != axi_pkg::RESP_DECERR) || (r.r_data != '0) || + (r.r_last != (beat == 1))) begin + $error("[DECODE] Invalid read beat %0d returned data=0x%016x last=%0b resp=%0d", + beat, r.r_data, r.r_last, r.r_resp); + end + end + + ax.ax_len = 0; + axi_drv.send_aw(ax); + w.w_data = '0; + w.w_strb = '1; + w.w_last = 1'b1; + axi_drv.send_w(w); + axi_drv.recv_b(b); + if (b.b_resp != axi_pkg::RESP_DECERR) begin + $error("[DECODE] Invalid write returned response %0d", b.b_resp); + end + + reg_drv.send_read(32'h54, status, reg_error); + if ((reg_error != 1'b0) || !status[0]) begin + $error("[DECODE] Sticky decode-error status was not set"); + end + reg_drv.send_write(32'h54, 32'h1, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + reg_drv.send_read(32'h54, status, reg_error); + if ((reg_error != 1'b0) || status[0]) begin + $error("[DECODE] Sticky decode-error status did not clear"); + end + endtask + + task automatic check_cross_chip_burst( + input axi_ctrl_master_t axi_drv, + input reg_bus_master_t reg_drv + ); + localparam axi_addr_t Boundary = axi_addr_t'(32'h8040_0000); + localparam axi_addr_t BurstAddr = Boundary - 16; + logic [31:0] segment_start_snapshot; + logic reg_error; + + if (NumChips < 2) begin + return; + end + + $display("==========================="); + $display("= Cross-chip burst ="); + $display("==========================="); + + reg_drv.send_write(32'h34, Boundary, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + reg_drv.send_write(32'h38, Boundary, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + + segment_start_snapshot = segment_start_count; + axi_write_slow(axi_drv, BurstAddr, 4, 0); + axi_read_slow_check(axi_drv, BurstAddr, 4, 0); + if ((segment_start_count - segment_start_snapshot) < 4) begin + $error("[CROSS-CHIP] Observed %0d segment starts, expected at least four", + segment_start_count - segment_start_snapshot); + end + + reg_drv.send_write(32'h38, 32'h8100_0000, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + reg_drv.send_write(32'h34, 32'h8100_0000, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + endtask + + task automatic check_large_rule_distance(input axi_ctrl_master_t axi_drv); + localparam axi_addr_t ReadAddr = axi_addr_t'(32'h802f_0002); + localparam int unsigned NumBeats = 121; + axi_ctrl_master_t::ax_beat_t ax = new(); + axi_ctrl_master_t::r_beat_t r; + + ax.ax_addr = ReadAddr; + ax.ax_id = '0; + ax.ax_len = NumBeats - 1; + ax.ax_size = 2; + ax.ax_burst = axi_pkg::BURST_INCR; + axi_drv.send_ar(ax); + for (int unsigned beat = 0; beat < NumBeats; beat++) begin + axi_drv.recv_r(r); + if ((r.r_resp != axi_pkg::RESP_OKAY) || (r.r_last != (beat == NumBeats - 1))) begin + $error("[AXI-RANGE] Beat %0d returned last=%0b resp=%0d", + beat, r.r_last, r.r_resp); + end + end + endtask + + task automatic check_range_edges( + input axi_ctrl_master_t axi_drv, + input reg_bus_master_t reg_drv + ); + localparam axi_addr_t ValidAddr = axi_addr_t'(32'h8100_0100); + localparam axi_addr_t OverflowAddr = axi_addr_t'(32'hffff_fff8); + localparam logic [63:0] TestData = 64'h0123_4567_89ab_cdef; + axi_ctrl_master_t::ax_beat_t ax = new(); + axi_ctrl_master_t::r_beat_t r; + logic reg_error; + + if (NumChips < 2) begin + return; + end + + $display("==========================="); + $display("= Address range edges ="); + $display("==========================="); + + // A zero bound extends the final rule through the end of the address space. + reg_drv.send_write(32'h3c, '0, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + axi_write_subword(axi_drv, ValidAddr, TestData, 3); + axi_check_subword(axi_drv, ValidAddr, TestData, 3); + + // The end address is exclusive; a burst extending beyond it must be rejected. + ax.ax_addr = OverflowAddr; + ax.ax_id = '0; + ax.ax_len = 1; + ax.ax_size = 3; + ax.ax_burst = axi_pkg::BURST_INCR; + axi_drv.send_ar(ax); + for (int unsigned beat = 0; beat < 2; beat++) begin + axi_drv.recv_r(r); + if ((r.r_resp != axi_pkg::RESP_DECERR) || + (r.r_last != (beat == 1))) begin + $error("[AXI-RANGE] Overflow beat %0d returned last=%0b resp=%0d", + beat, r.r_last, r.r_resp); + end + end + + reg_drv.send_write(32'h3c, 32'h8200_0000, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + endtask + + task automatic check_atomic_add(input axi_ctrl_master_t axi_drv); + localparam axi_addr_t AtomicAddr = axi_addr_t'(32'h8000_0200); + localparam logic [31:0] InitialValue = 32'h1234_5678; + localparam logic [31:0] Addend = 32'h0102_0304; + axi_ctrl_master_t::ax_beat_t ax = new(); + axi_ctrl_master_t::w_beat_t w = new(); + axi_ctrl_master_t::b_beat_t b; + axi_ctrl_master_t::r_beat_t r; + + $display("==========================="); + $display("= Atomic add ="); + $display("==========================="); + + axi_write_subword(axi_drv, AtomicAddr, InitialValue, 2); + + ax.ax_addr = AtomicAddr; + ax.ax_id = '0; + ax.ax_len = '0; + ax.ax_size = 2; + ax.ax_burst = axi_pkg::BURST_INCR; + ax.ax_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_ADD}; + axi_drv.send_aw(ax); + + w.w_data = subword_data(Addend, AtomicAddr); + w.w_strb = subword_strb(AtomicAddr, 2); + w.w_last = 1'b1; + axi_drv.send_w(w); + + fork + axi_drv.recv_b(b); + axi_drv.recv_r(r); + join + + if ((b.b_resp != axi_pkg::RESP_OKAY) || (r.r_resp != axi_pkg::RESP_OKAY) || + (r.r_data[31:0] != InitialValue) || !r.r_last) begin + $error("[ATOMIC] Add returned old=0x%08x rresp=%0d bresp=%0d last=%0b", + r.r_data[31:0], r.r_resp, b.b_resp, r.r_last); + end + axi_check_subword(axi_drv, AtomicAddr, InitialValue + Addend, 2); + + // Big-endian arithmetic is rejected explicitly and must still drain W. + ax.ax_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_BIG_END, + axi_pkg::ATOP_ADD}; + axi_drv.send_aw(ax); + axi_drv.send_w(w); + fork + axi_drv.recv_b(b); + axi_drv.recv_r(r); + join + if ((b.b_resp != axi_pkg::RESP_SLVERR) || (r.r_resp != axi_pkg::RESP_SLVERR)) begin + $error("[ATOMIC] Unsupported operation returned rresp=%0d bresp=%0d", + r.r_resp, b.b_resp); + end + axi_check_subword(axi_drv, AtomicAddr, InitialValue + Addend, 2); + + // A malformed multi-beat atomic must drain all W beats before returning an error. + ax.ax_len = 1; + ax.ax_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_ADD}; + axi_drv.send_aw(ax); + w.w_data = subword_data(32'hdead_beef, AtomicAddr); + w.w_strb = subword_strb(AtomicAddr, 2); + w.w_last = 1'b0; + axi_drv.send_w(w); + w.w_data = subword_data(32'hfeed_cafe, AtomicAddr); + w.w_last = 1'b1; + axi_drv.send_w(w); + fork + axi_drv.recv_b(b); + axi_drv.recv_r(r); + join + if ((b.b_resp != axi_pkg::RESP_SLVERR) || (r.r_resp != axi_pkg::RESP_SLVERR)) begin + $error("[ATOMIC] Multi-beat operation returned rresp=%0d bresp=%0d", + r.r_resp, b.b_resp); + end + axi_write_subword(axi_drv, AtomicAddr, 32'h89ab_cdef, 2); + axi_check_subword(axi_drv, AtomicAddr, 32'h89ab_cdef, 2); + endtask + + task automatic check_atomic_range_errors( + input axi_ctrl_master_t axi_drv, + input reg_bus_master_t reg_drv + ); + localparam axi_addr_t InvalidAddr = axi_addr_t'(32'h9000_0000); + localparam axi_addr_t ZeroEndAddr = axi_addr_t'(32'h8100_0400); + localparam int unsigned AtomicSize = NumPhys + 1; + localparam logic [31:0] ZeroEndInitial = 32'h1020_3040; + localparam logic [31:0] ZeroEndAddend = 32'h0101_0101; + axi_ctrl_master_t::ax_beat_t ax = new(); + axi_ctrl_master_t::w_beat_t w = new(); + axi_ctrl_master_t::b_beat_t b; + axi_ctrl_master_t::r_beat_t r; + logic [31:0] segment_start_snapshot; + logic reg_error; + + if (NumChips < 2) begin + return; + end + + $display("==========================="); + $display("= Atomic range errors ="); + $display("==========================="); + + ax.ax_addr = InvalidAddr; + ax.ax_id = '0; + ax.ax_len = '0; + ax.ax_size = AtomicSize; + ax.ax_burst = axi_pkg::BURST_INCR; + ax.ax_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_ADD}; + w.w_data = subword_data(64'hfedc_ba98_7654_3210, InvalidAddr); + w.w_strb = subword_strb(InvalidAddr, AtomicSize); + w.w_last = 1'b1; + + // Unmapped atomic loads still owe both the AXI R and B responses. + segment_start_snapshot = segment_start_count; + axi_drv.send_aw(ax); + axi_drv.send_w(w); + fork + axi_drv.recv_b(b); + axi_drv.recv_r(r); + join + if ((b.b_resp != axi_pkg::RESP_SLVERR) || (r.r_resp != axi_pkg::RESP_SLVERR) || + !r.r_last) begin + $error("[ATOMIC-RANGE] Unmapped operation returned rlast=%0b rresp=%0d bresp=%0d", + r.r_last, r.r_resp, b.b_resp); + end + if (segment_start_count != segment_start_snapshot) begin + $error("[ATOMIC-RANGE] Rejected unmapped operation issued a HyperBus command"); + end + + // A zero-ended final rule must contain ordinary atomic accesses. + reg_drv.send_write(32'h3c, '0, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + axi_write_subword(axi_drv, ZeroEndAddr, ZeroEndInitial, 2); + ax.ax_addr = ZeroEndAddr; + ax.ax_size = 2; + w.w_data = subword_data(ZeroEndAddend, ZeroEndAddr); + w.w_strb = subword_strb(ZeroEndAddr, 2); + axi_drv.send_aw(ax); + axi_drv.send_w(w); + fork + axi_drv.recv_b(b); + axi_drv.recv_r(r); + join + if ((b.b_resp != axi_pkg::RESP_OKAY) || (r.r_resp != axi_pkg::RESP_OKAY) || + (r.r_data[31:0] != ZeroEndInitial) || !r.r_last) begin + $error("[ATOMIC-RANGE] Zero-ended rule returned old=0x%08x rresp=%0d bresp=%0d", + r.r_data[31:0], r.r_resp, b.b_resp); + end + axi_check_subword(axi_drv, ZeroEndAddr, ZeroEndInitial + ZeroEndAddend, 2); + reg_drv.send_write(32'h3c, 32'h8200_0000, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + endtask + initial begin : proc_sim_crtl automatic axi_scoreboard_mst_t mst_scoreboard = new( score_mst_intf_dv ); @@ -519,6 +1031,18 @@ module axi_hyper_tb @(posedge rst_n); mst_scoreboard.monitor(); + // Map each chip to a distinct 16 MiB host-address window. + if (NumChips > 1) begin + reg_master.send_write(32'h3c, 32'h8200_0000, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_write(32'h38, 32'h8100_0000, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + end + reg_master.send_write(32'h34, 32'h8100_0000, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_write(32'h30, 32'h8000_0000, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + if (TbDutVariant != 0) begin reg_master.send_write(32'h4 << 2, TbRxDelayLineTaps, '1, s_reg_error); if (s_reg_error != 1'b0) $error("unexpected error"); @@ -528,7 +1052,19 @@ module axi_hyper_tb #600350ns; + run_performance_smoke(axi_ctrl_mst); run_slow_backpressure_test(axi_ctrl_mst, reg_master); + check_config_barrier(axi_ctrl_mst, reg_master); + check_decode_errors(axi_ctrl_mst, reg_master); + check_cross_chip_burst(axi_ctrl_mst, reg_master); + check_large_rule_distance(axi_ctrl_mst); + check_range_edges(axi_ctrl_mst, reg_master); + check_atomic_add(axi_ctrl_mst); + check_atomic_range_errors(axi_ctrl_mst, reg_master); + + if (NumPhys == 1) begin + check_unaligned_word_access(axi_ctrl_mst); + end if (TbDutVariant == 0) begin // switch memory address space to register space @@ -567,30 +1103,6 @@ module axi_hyper_tb $display("==========================="); reg_master.send_write(32'h20,1'b0,'1,s_reg_error); - reg_master.send_write(32'h24,1'b0,'1,s_reg_error); - if (s_reg_error != 1'b0) $error("unexpected error"); - - axi_rand_mst.reset(); - axi_ctrl_mst.reset_master(); - check_odd_subword_accesses(axi_ctrl_mst); - - $display("==========================="); - $display("= Random AXI transactions ="); - $display("==========================="); - - axi_rand_mst.run(TbNumReads, TbNumWrites); - - $display("==========================="); - $display("= Test finished ="); - $display("==========================="); - - mst_scoreboard.clear_range(32'h8000_0000, 32'h8000_0000 + ( TbDramDataWidth * TbDramLenWidth )); - - $display("==========================="); - $display("= Use only phy 1 ="); - $display("==========================="); - - reg_master.send_write(32'h24,1'b1,'1,s_reg_error); if (s_reg_error != 1'b0) $error("unexpected error"); axi_rand_mst.reset(); @@ -672,3 +1184,16 @@ module axi_hyper_tb_asynchronous; .TbTxDelayLineTaps ( 19 ) ) i_axi_hyper_tb (); endmodule + +module axi_hyper_tb_synchronous_one_phy; + axi_hyper_tb #( + .NumPhys ( 1 ), + .TbNumWrites ( 0 ), + .TbNumReads ( 0 ), + .TbDutVariant ( 1 ), + .TbCyclTime ( 10ns ), + .TbPhyCyclTime ( 10ns ), + .TbRxDelayLineTaps( 16 ), + .TbTxDelayLineTaps( 31 ) + ) i_axi_hyper_tb (); +endmodule diff --git a/test/dut_if.sv b/test/dut_if.sv index 65b016f..5075bf1 100644 --- a/test/dut_if.sv +++ b/test/dut_if.sv @@ -162,11 +162,6 @@ module dut_if .AxiUserWidth ( AxiUserWidth ), .axi_req_t ( axi_req_t ), .axi_rsp_t ( axi_resp_t ), - .axi_aw_chan_t ( axi_aw_chan_t ), - .axi_w_chan_t ( axi_w_chan_t ), - .axi_b_chan_t ( axi_b_chan_t ), - .axi_ar_chan_t ( axi_ar_chan_t ), - .axi_r_chan_t ( axi_r_chan_t ), .RegDataWidth ( RegDw ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), @@ -216,7 +211,7 @@ module dut_if .DQ1 ( pad_hyper_dq[i][1] ), .DQ0 ( pad_hyper_dq[i][0] ), .RWDS ( pad_hyper_rwds[i] ), - .CSNeg ( pad_hyper_csn[i][0] ), + .CSNeg ( pad_hyper_csn[i][j] ), .CK ( pad_hyper_ck[i] ), .CKNeg ( pad_hyper_ckn[i] ), .RESETNeg ( pad_hyper_reset[i] ) diff --git a/test/fixture_hyperbus.sv b/test/fixture_hyperbus.sv index e4ce27d..bb232a1 100644 --- a/test/fixture_hyperbus.sv +++ b/test/fixture_hyperbus.sv @@ -178,11 +178,6 @@ module fixture_hyperbus #( .AxiUserWidth ( 1 ), .axi_req_t ( req_t ), .axi_rsp_t ( resp_t ), - .axi_aw_chan_t ( aw_chan_t ), - .axi_w_chan_t ( w_chan_t ), - .axi_b_chan_t ( b_chan_t ), - .axi_ar_chan_t ( ar_chan_t ), - .axi_r_chan_t ( r_chan_t ), .RegDataWidth ( RegDw ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), diff --git a/test/hyperbus_cfg_regs_tb.sv b/test/hyperbus_cfg_regs_tb.sv index 2209dcb..5c6d4b0 100644 --- a/test/hyperbus_cfg_regs_tb.sv +++ b/test/hyperbus_cfg_regs_tb.sv @@ -24,7 +24,7 @@ module hyperbus_cfg_regs_tb; logic [31:0] start_addr; logic [31:0] end_addr; logic [2:0] idx; - } rule_t; + } addr_rule_t; logic clk; logic rst_n; @@ -32,12 +32,13 @@ module hyperbus_cfg_regs_tb; reg_rsp_t reg_rsp; hyperbus_pkg::frontend_cfg_t frontend_cfg; hyperbus_pkg::phy_cfg_t phy_cfg; - rule_t [NumChips-1:0] chip_rules; - logic trans_active; + addr_rule_t [NumChips-1:0] chip_rules; + logic decode_error; task automatic reg_write( input logic [7:0] addr, - input logic [31:0] data + input logic [31:0] data, + input logic inject_decode_error ); @(negedge clk); reg_req.addr = addr; @@ -45,6 +46,7 @@ module hyperbus_cfg_regs_tb; reg_req.wdata = data; reg_req.wstrb = '1; reg_req.valid = 1'b1; + decode_error = inject_decode_error; do begin @(posedge clk); @@ -55,6 +57,7 @@ module hyperbus_cfg_regs_tb; @(negedge clk); reg_req.valid = 1'b0; + decode_error = 1'b0; endtask task automatic reg_read( @@ -80,50 +83,59 @@ module hyperbus_cfg_regs_tb; reg_req.valid = 1'b0; endtask - always #5ns clk = ~clk; + initial begin + clk = 1'b0; + forever #5ns clk = ~clk; + end + + sim_timeout #( + .Cycles ( 100 ) + ) i_sim_timeout ( + .clk_i ( clk ), + .rst_ni ( rst_n ) + ); hyperbus_cfg_regs #( - .NumChips ( NumChips ), - .NumPhys ( 2 ), - .RegDataWidth ( 32 ), - .reg_req_t ( reg_req_t ), - .reg_rsp_t ( reg_rsp_t ), - .rule_t ( rule_t ) - ) i_dut ( - .clk_i ( clk ), - .rst_ni ( rst_n ), - .reg_req_i ( reg_req ), - .reg_rsp_o ( reg_rsp ), - .frontend_cfg_o ( frontend_cfg ), - .phy_cfg_o ( phy_cfg ), - .chip_rules_o ( chip_rules ), - .trans_active_i ( trans_active ) + .NumChips ( NumChips ), + .NumPhys ( 2 ), + .RegDataWidth ( 32 ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .addr_rule_t ( addr_rule_t ) + ) i_cfg_regs ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .reg_req_i ( reg_req ), + .reg_rsp_o ( reg_rsp ), + .frontend_cfg_o ( frontend_cfg ), + .phy_cfg_o ( phy_cfg ), + .chip_rules_o ( chip_rules ), + .decode_error_i ( decode_error ) ); initial begin : proc_stimulus + logic [31:0] status; logic [31:0] chip_addr; logic [31:0] rx_clk_delay; logic [31:0] tx_clk_delay; - clk = 1'b0; rst_n = 1'b0; reg_req = '0; - trans_active = 1'b0; - - repeat (4) @(posedge clk); + decode_error = 1'b0; + repeat (5) @(posedge clk); + @(negedge clk); rst_n = 1'b1; - repeat (2) @(posedge clk); - for (int unsigned i = 0; i < NumChips; i++) begin - if ((chip_rules[i].start_addr != i * 32'h0040_0000) || - (chip_rules[i].end_addr != (i + 1) * 32'h0040_0000)) begin - $error("Unexpected reset address range for chip %0d", i); - end + if ((chip_rules[0].start_addr != 32'h0000_0000) || + (chip_rules[0].end_addr != 32'h0040_0000) || + (chip_rules[7].start_addr != 32'h01c0_0000) || + (chip_rules[7].end_addr != 32'h0200_0000)) begin + $error("Unexpected reset chip address map"); end // Exercise both the fine [4:0] and coarse [7:5] delay settings. - reg_write(8'h10, 8'hb5); - reg_write(8'h14, 8'h6a); + reg_write(8'h10, 8'hb5, 1'b0); + reg_write(8'h14, 8'h6a, 1'b0); reg_read(8'h10, rx_clk_delay); reg_read(8'h14, tx_clk_delay); if ((rx_clk_delay != 8'hb5) || (tx_clk_delay != 8'h6a) || @@ -132,32 +144,26 @@ module hyperbus_cfg_regs_tb; $error("Delay-line configuration fields did not take effect"); end - // Low address bits are not stored and read back as zero. - reg_write(8'h70, 32'h8134_5678); + // Only address bits [31:22] are stored; low bits read back as zero. + reg_write(8'h70, 32'h8134_5678, 1'b0); reg_read(8'h70, chip_addr); if ((chip_addr != 32'h8100_0000) || (chip_rules[7].start_addr != 32'h8100_0000)) begin - $error("Chip address alignment was not applied"); + $error("Chip address alignment was not enforced"); end - // A new configuration access must wait for an active transfer to finish. - trans_active = 1'b1; - @(negedge clk); - reg_req.addr = 8'h00; - reg_req.write = 1'b0; - reg_req.valid = 1'b1; - repeat (3) begin - @(posedge clk); - if (reg_rsp.ready) begin - $error("Configuration access completed during an active transfer"); - end + // A new hardware event must win over a simultaneous software W1C. + reg_write(8'h54, 32'h1, 1'b1); + reg_read(8'h54, status); + if (!status[0]) begin + $error("Decode error was lost during a simultaneous W1C"); + end + + reg_write(8'h54, 32'h1, 1'b0); + reg_read(8'h54, status); + if (status[0]) begin + $error("Decode error did not clear without a new hardware event"); end - trans_active = 1'b0; - do begin - @(posedge clk); - end while (!reg_rsp.ready); - @(negedge clk); - reg_req.valid = 1'b0; $finish; end diff --git a/test/hyperbus_test_dut.sv b/test/hyperbus_test_dut.sv index a1134f6..f180c6c 100644 --- a/test/hyperbus_test_dut.sv +++ b/test/hyperbus_test_dut.sv @@ -12,14 +12,7 @@ module hyperbus_test_dut #( parameter int unsigned AxiUserWidth = -1, parameter type axi_req_t = logic, parameter type axi_rsp_t = logic, - parameter type axi_w_chan_t = logic, - parameter type axi_b_chan_t = logic, - parameter type axi_ar_chan_t = logic, - parameter type axi_r_chan_t = logic, - parameter type axi_aw_chan_t = logic, - parameter int unsigned RegAddrWidth = -1, parameter int unsigned RegDataWidth = -1, - parameter int unsigned MinFreqMHz = 100, parameter type reg_req_t = logic, parameter type reg_rsp_t = logic, parameter type axi_rule_t = logic @@ -65,14 +58,7 @@ module hyperbus_test_dut #( .AxiUserWidth ( AxiUserWidth ), .axi_req_t ( axi_req_t ), .axi_rsp_t ( axi_rsp_t ), - .axi_aw_chan_t ( axi_aw_chan_t ), - .axi_w_chan_t ( axi_w_chan_t ), - .axi_b_chan_t ( axi_b_chan_t ), - .axi_ar_chan_t ( axi_ar_chan_t ), - .axi_r_chan_t ( axi_r_chan_t ), - .RegAddrWidth ( RegAddrWidth ), .RegDataWidth ( RegDataWidth ), - .MinFreqMHz ( MinFreqMHz ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), .axi_rule_t ( axi_rule_t ) @@ -108,14 +94,7 @@ module hyperbus_test_dut #( .AxiUserWidth ( AxiUserWidth ), .axi_req_t ( axi_req_t ), .axi_rsp_t ( axi_rsp_t ), - .axi_aw_chan_t ( axi_aw_chan_t ), - .axi_w_chan_t ( axi_w_chan_t ), - .axi_b_chan_t ( axi_b_chan_t ), - .axi_ar_chan_t ( axi_ar_chan_t ), - .axi_r_chan_t ( axi_r_chan_t ), - .RegAddrWidth ( RegAddrWidth ), .RegDataWidth ( RegDataWidth ), - .MinFreqMHz ( MinFreqMHz ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), .axi_rule_t ( axi_rule_t ) @@ -151,14 +130,7 @@ module hyperbus_test_dut #( .AxiUserWidth ( AxiUserWidth ), .axi_req_t ( axi_req_t ), .axi_rsp_t ( axi_rsp_t ), - .axi_aw_chan_t ( axi_aw_chan_t ), - .axi_w_chan_t ( axi_w_chan_t ), - .axi_b_chan_t ( axi_b_chan_t ), - .axi_ar_chan_t ( axi_ar_chan_t ), - .axi_r_chan_t ( axi_r_chan_t ), - .RegAddrWidth ( RegAddrWidth ), .RegDataWidth ( RegDataWidth ), - .MinFreqMHz ( MinFreqMHz ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), .axi_rule_t ( axi_rule_t ) From 92c044930224bb4ee6a1438d3dbb9a8c73bd373b Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Thu, 23 Jul 2026 17:19:06 +0200 Subject: [PATCH 08/11] Refactor HyperBus datapath interfaces Use reusable host and HyperBus stream structures across the midend and bridges. Isolate atomic read-modify-write sequencing and replace the legacy PHY adapters with explicit read and write adapters. --- Bender.yml | 6 +- include/hyperbus/typedef.svh | 65 +- src/hyperbus_async_bridge.sv | 156 ++-- src/hyperbus_asynchronous.sv | 162 +--- src/hyperbus_atomic_handler.sv | 279 +++++++ src/hyperbus_axi.sv | 149 ++-- src/hyperbus_backend.sv | 258 ++++-- src/hyperbus_iso_bridge.sv | 158 ++-- src/hyperbus_isochronous.sv | 162 +--- src/hyperbus_midend.sv | 742 ++++++------------ src/hyperbus_phy_if.sv | 261 ------ ...rbus_phy2r.sv => hyperbus_read_adapter.sv} | 95 ++- src/hyperbus_synchronous.sv | 146 +--- ...bus_w2phy.sv => hyperbus_write_adapter.sv} | 86 +- 14 files changed, 1199 insertions(+), 1526 deletions(-) create mode 100644 src/hyperbus_atomic_handler.sv delete mode 100644 src/hyperbus_phy_if.sv rename src/{hyperbus_phy2r.sv => hyperbus_read_adapter.sv} (65%) rename src/{hyperbus_w2phy.sv => hyperbus_write_adapter.sv} (70%) diff --git a/Bender.yml b/Bender.yml index ada1701..18967ea 100644 --- a/Bender.yml +++ b/Bender.yml @@ -49,15 +49,15 @@ sources: - src/hyperbus_pkg.sv - src/hyperbus_clk_gen.sv - src/hyperbus_clock_diff_out.sv - - src/hyperbus_w2phy.sv - - src/hyperbus_phy2r.sv + - src/hyperbus_write_adapter.sv + - src/hyperbus_read_adapter.sv - src/hyperbus_ddr_out.sv - src/hyperbus_trx.sv - src/regs/hyperbus_cfg_regblock.sv - src/hyperbus_cfg_regs.sv - src/hyperbus_phy.sv - - src/hyperbus_phy_if.sv - src/hyperbus_axi.sv + - src/hyperbus_atomic_handler.sv - src/hyperbus_midend.sv - src/hyperbus_cfg_frontend.sv - src/hyperbus_async_bridge.sv diff --git a/include/hyperbus/typedef.svh b/include/hyperbus/typedef.svh index 29193bf..9474b9f 100644 --- a/include/hyperbus/typedef.svh +++ b/include/hyperbus/typedef.svh @@ -5,7 +5,7 @@ `ifndef HYPERBUS_TYPEDEF_SVH_ `define HYPERBUS_TYPEDEF_SVH_ -`define HYPERBUS_TYPEDEF_HOST_REQ_T(__name, __addr_t) \ +`define HYPERBUS_TYPEDEF_HOST_CMD_T(__name, __addr_t) \ typedef struct packed { \ logic write; \ __addr_t addr; \ @@ -38,10 +38,69 @@ logic atomic_ok; \ } __name; +`define HYPERBUS_TYPEDEF_HOST_REQ_T(__name, __cmd_t, __w_t) \ + typedef struct packed { \ + __cmd_t cmd; \ + logic cmd_valid; \ + __w_t w; \ + logic w_valid; \ + logic r_ready; \ + logic wrsp_ready; \ + } __name; + +`define HYPERBUS_TYPEDEF_HOST_RSP_T(__name, __r_t, __wrsp_t) \ + typedef struct packed { \ + logic cmd_ready; \ + logic w_ready; \ + __r_t r; \ + logic r_valid; \ + __wrsp_t wrsp; \ + logic wrsp_valid; \ + } __name; + `define HYPERBUS_TYPEDEF_HOST_ALL_CT(__name, __addr_t, __data_t, __strb_t) \ - `HYPERBUS_TYPEDEF_HOST_REQ_T(__name``_req_t, __addr_t) \ + `HYPERBUS_TYPEDEF_HOST_CMD_T(__name``_cmd_t, __addr_t) \ `HYPERBUS_TYPEDEF_HOST_W_T(__name``_w_t, __data_t, __strb_t) \ `HYPERBUS_TYPEDEF_HOST_R_T(__name``_r_t, __data_t) \ - `HYPERBUS_TYPEDEF_HOST_WRSP_T(__name``_wrsp_t) + `HYPERBUS_TYPEDEF_HOST_WRSP_T(__name``_wrsp_t) \ + `HYPERBUS_TYPEDEF_HOST_REQ_T(__name``_req_t, __name``_cmd_t, \ + __name``_w_t) \ + `HYPERBUS_TYPEDEF_HOST_RSP_T(__name``_rsp_t, __name``_r_t, \ + __name``_wrsp_t) + +`define HYPERBUS_TYPEDEF_LINK_ALL_CT(__name, __num_phys, __num_chips) \ + typedef struct packed { \ + logic [(16*__num_phys)-1:0] data; \ + logic last; \ + logic [(2*__num_phys)-1:0] strb; \ + } __name``_tx_t; \ + typedef struct packed { \ + logic [(16*__num_phys)-1:0] data; \ + logic last; \ + logic error; \ + } __name``_rx_t; \ + typedef struct packed { \ + logic error; \ + } __name``_wrsp_t; \ + typedef struct packed { \ + hyperbus_pkg::hyper_tf_t trans; \ + logic [__num_chips-1:0] cs; \ + } __name``_cmd_t; \ + typedef struct packed { \ + __name``_cmd_t cmd; \ + logic cmd_valid; \ + __name``_tx_t tx; \ + logic tx_valid; \ + logic rx_ready; \ + logic wrsp_ready; \ + } __name``_req_t; \ + typedef struct packed { \ + logic cmd_ready; \ + logic tx_ready; \ + __name``_rx_t rx; \ + logic rx_valid; \ + __name``_wrsp_t wrsp; \ + logic wrsp_valid; \ + } __name``_rsp_t; `endif diff --git a/src/hyperbus_async_bridge.sv b/src/hyperbus_async_bridge.sv index 3742701..9d97ed2 100644 --- a/src/hyperbus_async_bridge.sv +++ b/src/hyperbus_async_bridge.sv @@ -11,7 +11,10 @@ module hyperbus_async_bridge #( parameter int unsigned CdcSyncStages = 3, parameter type hyper_rx_t = logic, parameter type hyper_tx_t = logic, - parameter type hyper_cmd_t = logic + parameter type hyper_wrsp_t = logic, + parameter type hyper_cmd_t = logic, + parameter type hyper_req_t = logic, + parameter type hyper_rsp_t = logic ) ( input logic clk_sys_i, input logic rst_sys_ni, @@ -23,38 +26,17 @@ module hyperbus_async_bridge #( output logic frontend_cfg_apply_ready_o, output logic frontend_cfg_apply_done_o, - output hyper_rx_t frontend_rx_o, - output logic frontend_rx_valid_o, - input logic frontend_rx_ready_i, - input hyper_tx_t frontend_tx_i, - input logic frontend_tx_valid_i, - output logic frontend_tx_ready_o, - output logic frontend_wrsp_error_o, - output logic frontend_wrsp_valid_o, - input logic frontend_wrsp_ready_i, - input hyper_cmd_t frontend_cmd_i, - input logic frontend_cmd_valid_i, - output logic frontend_cmd_ready_o, - - input hyper_rx_t backend_rx_i, - input logic backend_rx_valid_i, - output logic backend_rx_ready_o, - output hyper_tx_t backend_tx_o, - output logic backend_tx_valid_o, - input logic backend_tx_ready_i, - input logic backend_wrsp_error_i, - input logic backend_wrsp_valid_i, - output logic backend_wrsp_ready_o, - output hyper_cmd_t backend_cmd_o, - output logic backend_cmd_valid_o, - input logic backend_cmd_ready_i, + input hyper_req_t frontend_req_i, + output hyper_rsp_t frontend_rsp_o, + output hyper_req_t backend_req_o, + input hyper_rsp_t backend_rsp_i, output hyperbus_pkg::phy_cfg_t cfg_apply_o, output logic cfg_apply_valid_o, input logic cfg_apply_ready_i ); - logic cfg_apply_src_fire; + logic cfg_apply_accepted; logic cfg_apply_pending_d, cfg_apply_pending_q; `ASSERT_INIT(CdcSyncStagesValid, CdcSyncStages >= 3) @@ -62,15 +44,15 @@ module hyperbus_async_bridge #( `ASSERT_INIT(TxCdcFifoDepthValid, (1 << TxFifoLogDepth) > (2 * CdcSyncStages)) `ASSERT_INIT(RxCdcFifoDepthValid, (1 << RxFifoLogDepth) > (2 * CdcSyncStages)) - assign cfg_apply_src_fire = + assign cfg_apply_accepted = frontend_cfg_apply_valid_i && frontend_cfg_apply_ready_o; assign frontend_cfg_apply_done_o = cfg_apply_pending_q && frontend_cfg_apply_ready_o; - always_comb begin + always_comb begin : proc_cfg_apply_pending cfg_apply_pending_d = cfg_apply_pending_q; - if (cfg_apply_src_fire) begin + if (cfg_apply_accepted) begin cfg_apply_pending_d = 1'b1; end else if (frontend_cfg_apply_done_o) begin cfg_apply_pending_d = 1'b0; @@ -103,40 +85,40 @@ module hyperbus_async_bridge #( .T ( hyper_cmd_t ), .SYNC_STAGES ( CdcSyncStages ) ) i_cdc_cmd ( - .src_rst_ni ( rst_sys_ni ), - .src_clk_i ( clk_sys_i ), - .src_clear_i ( 1'b0 ), - .src_clear_pending_o ( ), - .src_data_i ( frontend_cmd_i ), - .src_valid_i ( frontend_cmd_valid_i ), - .src_ready_o ( frontend_cmd_ready_o ), - .dst_rst_ni ( rst_phy_ni ), - .dst_clk_i ( clk_phy_i ), - .dst_clear_i ( 1'b0 ), - .dst_clear_pending_o ( ), - .dst_data_o ( backend_cmd_o ), - .dst_valid_o ( backend_cmd_valid_o ), - .dst_ready_i ( backend_cmd_ready_i ) + .src_rst_ni ( rst_sys_ni ), + .src_clk_i ( clk_sys_i ), + .src_clear_i ( 1'b0 ), + .src_clear_pending_o ( ), + .src_data_i ( frontend_req_i.cmd ), + .src_valid_i ( frontend_req_i.cmd_valid ), + .src_ready_o ( frontend_rsp_o.cmd_ready ), + .dst_rst_ni ( rst_phy_ni ), + .dst_clk_i ( clk_phy_i ), + .dst_clear_i ( 1'b0 ), + .dst_clear_pending_o ( ), + .dst_data_o ( backend_req_o.cmd ), + .dst_valid_o ( backend_req_o.cmd_valid ), + .dst_ready_i ( backend_rsp_i.cmd_ready ) ); cdc_2phase_clearable #( - .T ( logic ), + .T ( hyper_wrsp_t ), .SYNC_STAGES ( CdcSyncStages ) ) i_cdc_wrsp ( - .src_rst_ni ( rst_phy_ni ), - .src_clk_i ( clk_phy_i ), - .src_clear_i ( 1'b0 ), - .src_clear_pending_o ( ), - .src_data_i ( backend_wrsp_error_i ), - .src_valid_i ( backend_wrsp_valid_i ), - .src_ready_o ( backend_wrsp_ready_o ), - .dst_rst_ni ( rst_sys_ni ), - .dst_clk_i ( clk_sys_i ), - .dst_clear_i ( 1'b0 ), - .dst_clear_pending_o ( ), - .dst_data_o ( frontend_wrsp_error_o ), - .dst_valid_o ( frontend_wrsp_valid_o ), - .dst_ready_i ( frontend_wrsp_ready_i ) + .src_rst_ni ( rst_phy_ni ), + .src_clk_i ( clk_phy_i ), + .src_clear_i ( 1'b0 ), + .src_clear_pending_o ( ), + .src_data_i ( backend_rsp_i.wrsp ), + .src_valid_i ( backend_rsp_i.wrsp_valid ), + .src_ready_o ( backend_req_o.wrsp_ready ), + .dst_rst_ni ( rst_sys_ni ), + .dst_clk_i ( clk_sys_i ), + .dst_clear_i ( 1'b0 ), + .dst_clear_pending_o ( ), + .dst_data_o ( frontend_rsp_o.wrsp ), + .dst_valid_o ( frontend_rsp_o.wrsp_valid ), + .dst_ready_i ( frontend_req_i.wrsp_ready ) ); cdc_fifo_gray_clearable #( @@ -144,20 +126,20 @@ module hyperbus_async_bridge #( .LOG_DEPTH ( TxFifoLogDepth ), .SYNC_STAGES ( CdcSyncStages ) ) i_cdc_fifo_tx ( - .src_rst_ni ( rst_sys_ni ), - .src_clk_i ( clk_sys_i ), - .src_clear_i ( 1'b0 ), - .src_clear_pending_o ( ), - .src_data_i ( frontend_tx_i ), - .src_valid_i ( frontend_tx_valid_i ), - .src_ready_o ( frontend_tx_ready_o ), - .dst_rst_ni ( rst_phy_ni ), - .dst_clk_i ( clk_phy_i ), - .dst_clear_i ( 1'b0 ), - .dst_clear_pending_o ( ), - .dst_data_o ( backend_tx_o ), - .dst_valid_o ( backend_tx_valid_o ), - .dst_ready_i ( backend_tx_ready_i ) + .src_rst_ni ( rst_sys_ni ), + .src_clk_i ( clk_sys_i ), + .src_clear_i ( 1'b0 ), + .src_clear_pending_o ( ), + .src_data_i ( frontend_req_i.tx ), + .src_valid_i ( frontend_req_i.tx_valid ), + .src_ready_o ( frontend_rsp_o.tx_ready ), + .dst_rst_ni ( rst_phy_ni ), + .dst_clk_i ( clk_phy_i ), + .dst_clear_i ( 1'b0 ), + .dst_clear_pending_o ( ), + .dst_data_o ( backend_req_o.tx ), + .dst_valid_o ( backend_req_o.tx_valid ), + .dst_ready_i ( backend_rsp_i.tx_ready ) ); cdc_fifo_gray_clearable #( @@ -165,20 +147,20 @@ module hyperbus_async_bridge #( .LOG_DEPTH ( RxFifoLogDepth ), .SYNC_STAGES ( CdcSyncStages ) ) i_cdc_fifo_rx ( - .src_rst_ni ( rst_phy_ni ), - .src_clk_i ( clk_phy_i ), - .src_clear_i ( 1'b0 ), - .src_clear_pending_o ( ), - .src_data_i ( backend_rx_i ), - .src_valid_i ( backend_rx_valid_i ), - .src_ready_o ( backend_rx_ready_o ), - .dst_rst_ni ( rst_sys_ni ), - .dst_clk_i ( clk_sys_i ), - .dst_clear_i ( 1'b0 ), - .dst_clear_pending_o ( ), - .dst_data_o ( frontend_rx_o ), - .dst_valid_o ( frontend_rx_valid_o ), - .dst_ready_i ( frontend_rx_ready_i ) + .src_rst_ni ( rst_phy_ni ), + .src_clk_i ( clk_phy_i ), + .src_clear_i ( 1'b0 ), + .src_clear_pending_o ( ), + .src_data_i ( backend_rsp_i.rx ), + .src_valid_i ( backend_rsp_i.rx_valid ), + .src_ready_o ( backend_req_o.rx_ready ), + .dst_rst_ni ( rst_sys_ni ), + .dst_clk_i ( clk_sys_i ), + .dst_clear_i ( 1'b0 ), + .dst_clear_pending_o ( ), + .dst_data_o ( frontend_rsp_o.rx ), + .dst_valid_o ( frontend_rsp_o.rx_valid ), + .dst_ready_i ( frontend_req_i.rx_ready ) ); endmodule diff --git a/src/hyperbus_asynchronous.sv b/src/hyperbus_asynchronous.sv index f332340..ab26d99 100644 --- a/src/hyperbus_asynchronous.sv +++ b/src/hyperbus_asynchronous.sv @@ -53,27 +53,11 @@ module hyperbus_asynchronous #( `ASSERT_INIT(AxiAddrWidthValid, AxiAddrWidth >= $clog2(AxiDataWidth / 8)) - typedef struct packed { - logic [(16*NumPhys)-1:0] data; - logic last; - logic [(2*NumPhys)-1:0] strb; - } hyper_tx_t; - - typedef struct packed { - logic [(16*NumPhys)-1:0] data; - logic last; - logic error; - } hyper_rx_t; - - typedef struct packed { - hyperbus_pkg::hyper_tf_t trans; - logic [NumChips-1:0] cs; - } hyper_cmd_t; - typedef logic [AxiAddrWidth-1:0] host_addr_t; typedef logic [AxiDataWidth-1:0] host_data_t; typedef logic [AxiDataWidth/8-1:0] host_strb_t; `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) + `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys, NumChips) logic clk_backend; logic clk_backend_90; @@ -93,45 +77,13 @@ module hyperbus_asynchronous #( axi_rule_t [NumChips-1:0] frontend_chip_rules; host_req_t host_req; - logic host_req_valid; - logic host_req_ready; - host_w_t host_w; - logic host_w_valid; - logic host_w_ready; - host_r_t host_r; - logic host_r_valid; - logic host_r_ready; - host_wrsp_t host_wrsp; - logic host_wrsp_valid; - logic host_wrsp_ready; - - hyper_rx_t midend_rx; - logic midend_rx_valid; - logic midend_rx_ready; - hyper_tx_t midend_tx; - logic midend_tx_valid; - logic midend_tx_ready; - logic midend_wrsp_error; - logic midend_wrsp_valid; - logic midend_wrsp_ready; - hyper_cmd_t midend_cmd; - logic midend_cmd_valid; - logic midend_cmd_ready; + host_rsp_t host_rsp; + hyper_req_t midend_req; + hyper_rsp_t midend_rsp; logic midend_trans_active; logic midend_decode_error; - - hyper_rx_t backend_rx; - logic backend_rx_valid; - logic backend_rx_ready; - hyper_tx_t backend_tx; - logic backend_tx_valid; - logic backend_tx_ready; - logic backend_wrsp_error; - logic backend_wrsp_valid; - logic backend_wrsp_ready; - hyper_cmd_t backend_cmd; - logic backend_cmd_valid; - logic backend_cmd_ready; + hyper_req_t backend_req; + hyper_rsp_t backend_rsp; logic rst_phy_async_n; @@ -193,9 +145,7 @@ module hyperbus_asynchronous #( .axi_req_t ( axi_req_t ), .axi_rsp_t ( axi_rsp_t ), .host_req_t ( host_req_t ), - .host_w_t ( host_w_t ), - .host_r_t ( host_r_t ), - .host_wrsp_t ( host_wrsp_t ) + .host_rsp_t ( host_rsp_t ) ) i_axi_frontend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), @@ -203,18 +153,8 @@ module hyperbus_asynchronous #( .idle_o ( host_idle ), .axi_req_i ( axi_req_i ), .axi_rsp_o ( axi_rsp_o ), - .host_req_o ( host_req ), - .host_req_valid_o ( host_req_valid ), - .host_req_ready_i ( host_req_ready ), - .host_w_o ( host_w ), - .host_w_valid_o ( host_w_valid ), - .host_w_ready_i ( host_w_ready ), - .host_r_i ( host_r ), - .host_r_valid_i ( host_r_valid ), - .host_r_ready_o ( host_r_ready ), - .host_wrsp_i ( host_wrsp ), - .host_wrsp_valid_i ( host_wrsp_valid ), - .host_wrsp_ready_o ( host_wrsp_ready ) + .host_req_o ( host_req ), + .host_rsp_i ( host_rsp ) ); hyperbus_midend #( @@ -222,45 +162,29 @@ module hyperbus_asynchronous #( .HostDataWidth ( AxiDataWidth ), .NumChips ( NumChips ), .NumPhys ( NumPhys ), - .host_req_t ( host_req_t ), + .host_cmd_t ( host_cmd_t ), .host_w_t ( host_w_t ), .host_r_t ( host_r_t ), .host_wrsp_t ( host_wrsp_t ), + .host_req_t ( host_req_t ), + .host_rsp_t ( host_rsp_t ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), .hyper_cmd_t ( hyper_cmd_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ), .rule_t ( axi_rule_t ) ) i_midend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), - .host_req_i ( host_req ), - .host_req_valid_i ( host_req_valid ), - .host_req_ready_o ( host_req_ready ), - .host_w_i ( host_w ), - .host_w_valid_i ( host_w_valid ), - .host_w_ready_o ( host_w_ready ), - .host_r_o ( host_r ), - .host_r_valid_o ( host_r_valid ), - .host_r_ready_i ( host_r_ready ), - .host_wrsp_o ( host_wrsp ), - .host_wrsp_valid_o ( host_wrsp_valid ), - .host_wrsp_ready_i ( host_wrsp_ready ), + .host_link_req_i ( host_req ), + .host_link_rsp_o ( host_rsp ), .frontend_cfg_i ( frontend_cfg ), .chip_rules_i ( frontend_chip_rules ), .trans_active_o ( midend_trans_active ), .decode_error_o ( midend_decode_error ), - .rx_i ( midend_rx ), - .rx_valid_i ( midend_rx_valid ), - .rx_ready_o ( midend_rx_ready ), - .tx_o ( midend_tx ), - .tx_valid_o ( midend_tx_valid ), - .tx_ready_i ( midend_tx_ready ), - .wrsp_error_i ( midend_wrsp_error ), - .wrsp_valid_i ( midend_wrsp_valid ), - .wrsp_ready_o ( midend_wrsp_ready ), - .cmd_o ( midend_cmd ), - .cmd_valid_o ( midend_cmd_valid ), - .cmd_ready_i ( midend_cmd_ready ) + .hyper_link_req_o ( midend_req ), + .hyper_link_rsp_i ( midend_rsp ) ); hyperbus_async_bridge #( @@ -269,7 +193,10 @@ module hyperbus_asynchronous #( .CdcSyncStages ( CdcSyncStages ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), - .hyper_cmd_t ( hyper_cmd_t ) + .hyper_wrsp_t ( hyper_wrsp_t ), + .hyper_cmd_t ( hyper_cmd_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ) ) i_bridge ( .clk_sys_i ( clk_sys_i ), .rst_sys_ni ( rst_sys_ni ), @@ -279,30 +206,10 @@ module hyperbus_asynchronous #( .frontend_cfg_apply_valid_i ( frontend_cfg_apply_valid ), .frontend_cfg_apply_ready_o ( frontend_cfg_apply_ready ), .frontend_cfg_apply_done_o ( frontend_cfg_apply_done ), - .frontend_rx_o ( midend_rx ), - .frontend_rx_valid_o ( midend_rx_valid ), - .frontend_rx_ready_i ( midend_rx_ready ), - .frontend_tx_i ( midend_tx ), - .frontend_tx_valid_i ( midend_tx_valid ), - .frontend_tx_ready_o ( midend_tx_ready ), - .frontend_wrsp_error_o ( midend_wrsp_error ), - .frontend_wrsp_valid_o ( midend_wrsp_valid ), - .frontend_wrsp_ready_i ( midend_wrsp_ready ), - .frontend_cmd_i ( midend_cmd ), - .frontend_cmd_valid_i ( midend_cmd_valid ), - .frontend_cmd_ready_o ( midend_cmd_ready ), - .backend_rx_i ( backend_rx ), - .backend_rx_valid_i ( backend_rx_valid ), - .backend_rx_ready_o ( backend_rx_ready ), - .backend_tx_o ( backend_tx ), - .backend_tx_valid_o ( backend_tx_valid ), - .backend_tx_ready_i ( backend_tx_ready ), - .backend_wrsp_error_i ( backend_wrsp_error ), - .backend_wrsp_valid_i ( backend_wrsp_valid ), - .backend_wrsp_ready_o ( backend_wrsp_ready ), - .backend_cmd_o ( backend_cmd ), - .backend_cmd_valid_o ( backend_cmd_valid ), - .backend_cmd_ready_i ( backend_cmd_ready ), + .frontend_req_i ( midend_req ), + .frontend_rsp_o ( midend_rsp ), + .backend_req_o ( backend_req ), + .backend_rsp_i ( backend_rsp ), .cfg_apply_o ( backend_cfg_apply ), .cfg_apply_valid_o ( backend_cfg_apply_valid ), .cfg_apply_ready_i ( backend_cfg_apply_ready ) @@ -315,7 +222,8 @@ module hyperbus_asynchronous #( .SyncStages ( SyncStages ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), - .hyper_cmd_t ( hyper_cmd_t ) + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ) ) i_backend ( .clk_i ( clk_backend ), .clk_90_i ( clk_backend_90 ), @@ -326,18 +234,8 @@ module hyperbus_asynchronous #( .cfg_apply_ready_o ( backend_cfg_apply_ready ), .busy_o ( ), .tx_clk_delay_o ( backend_tx_clk_delay ), - .rx_o ( backend_rx ), - .rx_valid_o ( backend_rx_valid ), - .rx_ready_i ( backend_rx_ready ), - .tx_i ( backend_tx ), - .tx_valid_i ( backend_tx_valid ), - .tx_ready_o ( backend_tx_ready ), - .wrsp_error_o ( backend_wrsp_error ), - .wrsp_valid_o ( backend_wrsp_valid ), - .wrsp_ready_i ( backend_wrsp_ready ), - .cmd_i ( backend_cmd ), - .cmd_valid_i ( backend_cmd_valid ), - .cmd_ready_o ( backend_cmd_ready ), + .req_i ( backend_req ), + .rsp_o ( backend_rsp ), .hyper_cs_no ( hyper_cs_no ), .hyper_ck_o ( hyper_ck_o ), .hyper_ck_no ( hyper_ck_no ), diff --git a/src/hyperbus_atomic_handler.sv b/src/hyperbus_atomic_handler.sv new file mode 100644 index 0000000..4daddea --- /dev/null +++ b/src/hyperbus_atomic_handler.sv @@ -0,0 +1,279 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +`include "common_cells/registers.svh" + +module hyperbus_atomic_handler #( + parameter int unsigned HostAddrWidth = -1, + parameter int unsigned HostDataWidth = -1, + parameter type host_cmd_t = logic, + parameter type host_w_t = logic, + parameter type host_r_t = logic, + parameter type host_wrsp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + + input logic start_i, + input logic request_valid_i, + input host_cmd_t request_i, + output logic active_o, + output logic completed_o, + + output host_cmd_t command_o, + output logic command_valid_o, + input logic command_ready_i, + + input host_w_t host_w_i, + input logic host_w_valid_i, + output logic host_w_ready_o, + output host_r_t host_r_o, + output logic host_r_valid_o, + input logic host_r_ready_i, + output host_wrsp_t host_wrsp_o, + output logic host_wrsp_valid_o, + input logic host_wrsp_ready_i, + + input host_r_t read_i, + input logic read_valid_i, + output logic read_ready_o, + output host_w_t write_o, + output logic write_valid_o, + input logic write_ready_i, + input logic write_rsp_error_i, + input logic write_rsp_valid_i, + output logic write_rsp_ready_o +); + + localparam int unsigned HostDataBytes = HostDataWidth / 8; + localparam int unsigned HostBusAddrWidth = $clog2(HostDataBytes); + + typedef logic [HostAddrWidth-1:0] host_addr_t; + typedef logic [HostDataWidth-1:0] host_data_t; + + typedef enum logic [2:0] { + Idle, + WaitWriteData, + ReadCommand, + ReadData, + WriteCommand, + WriteData, + WriteResponse, + ReturnResponse + } atomic_state_e; + + typedef struct packed { + host_addr_t addr; + hyperbus_pkg::hyper_host_size_t size; + hyperbus_pkg::hyper_atomic_op_e atomic_op; + logic atomic_return; + } atomic_request_t; + + atomic_state_e state_d, state_q; + atomic_request_t request_d, request_q; + host_data_t operand_d, operand_q; + host_data_t read_data_d, read_data_q; + hyperbus_pkg::hyper_resp_e response_d, response_q; + logic read_pending_d, read_pending_q; + logic write_pending_d, write_pending_q; + + host_data_t value_mask; + host_data_t old_value; + host_data_t operand_value; + host_data_t swap_value; + host_data_t result_value; + host_data_t write_data; + int unsigned byte_offset; + int unsigned operand_bytes; + int unsigned operand_bits; + + assign active_o = state_q != Idle; + assign command_valid_o = (state_q == ReadCommand) || (state_q == WriteCommand); + assign completed_o = (state_q == ReturnResponse) && + (!read_pending_q || (host_r_valid_o && host_r_ready_i)) && + (!write_pending_q || (host_wrsp_valid_o && host_wrsp_ready_i)); + + always_comb begin : proc_command + command_o = '0; + command_o.write = state_q == WriteCommand; + command_o.addr = request_q.addr; + command_o.beats = 1; + command_o.size = request_q.size; + command_o.burst = hyperbus_pkg::HyperBurstIncr; + command_o.atomic_op = hyperbus_pkg::HyperAtomicNone; + command_o.atomic_return = 1'b0; + command_o.ordered = 1'b1; + if ((request_q.atomic_op == hyperbus_pkg::HyperAtomicCompare) && + (request_q.size != '0)) begin + command_o.size = request_q.size - 1'b1; + end + end + + always_comb begin : proc_alu + byte_offset = 0; + operand_bytes = 1; + operand_bits = 8; + value_mask = '1; + old_value = read_data_q; + operand_value = operand_q; + swap_value = operand_q >> 8; + result_value = old_value; + write_data = read_data_q; + + if (response_q == hyperbus_pkg::HyperRespOkay) begin + byte_offset = unsigned'(request_q.addr[HostBusAddrWidth-1:0]); + operand_bytes = 1 << ((request_q.atomic_op == hyperbus_pkg::HyperAtomicCompare) ? + (request_q.size - 1'b1) : request_q.size); + operand_bits = operand_bytes * 8; + if (operand_bits < HostDataWidth) begin + value_mask = value_mask >> (HostDataWidth - operand_bits); + end + + old_value = read_data_q >> (byte_offset * 8); + operand_value = operand_q >> (byte_offset * 8); + swap_value = operand_value >> operand_bits; + result_value = old_value; + + unique case (request_q.atomic_op) + hyperbus_pkg::HyperAtomicSwap: result_value = operand_value; + hyperbus_pkg::HyperAtomicCompare: begin + if ((old_value & value_mask) == (operand_value & value_mask)) begin + result_value = swap_value; + end + end + hyperbus_pkg::HyperAtomicAdd: result_value = old_value + operand_value; + hyperbus_pkg::HyperAtomicClear: result_value = old_value & ~operand_value; + hyperbus_pkg::HyperAtomicXor: result_value = old_value ^ operand_value; + hyperbus_pkg::HyperAtomicSet: result_value = old_value | operand_value; + hyperbus_pkg::HyperAtomicSignedMax: begin + if ((old_value[operand_bits-1] && !operand_value[operand_bits-1]) || + ((old_value[operand_bits-1] == operand_value[operand_bits-1]) && + ((old_value & value_mask) < (operand_value & value_mask)))) begin + result_value = operand_value; + end + end + hyperbus_pkg::HyperAtomicSignedMin: begin + if ((!old_value[operand_bits-1] && operand_value[operand_bits-1]) || + ((old_value[operand_bits-1] == operand_value[operand_bits-1]) && + ((old_value & value_mask) > (operand_value & value_mask)))) begin + result_value = operand_value; + end + end + hyperbus_pkg::HyperAtomicUnsignedMax: begin + if ((old_value & value_mask) < (operand_value & value_mask)) begin + result_value = operand_value; + end + end + hyperbus_pkg::HyperAtomicUnsignedMin: begin + if ((old_value & value_mask) > (operand_value & value_mask)) begin + result_value = operand_value; + end + end + default:; + endcase + + write_data = read_data_q & ~(value_mask << (byte_offset * 8)); + write_data |= (result_value & value_mask) << (byte_offset * 8); + end + end + + always_comb begin : proc_outputs + host_w_ready_o = state_q == WaitWriteData; + host_r_o = '0; + host_r_o.data = read_data_q; + host_r_o.resp = response_q; + host_r_o.last = 1'b1; + host_r_o.atomic_ok = response_q == hyperbus_pkg::HyperRespOkay; + host_r_valid_o = (state_q == ReturnResponse) && read_pending_q; + host_wrsp_o = '0; + host_wrsp_o.resp = response_q; + host_wrsp_o.atomic_ok = response_q == hyperbus_pkg::HyperRespOkay; + host_wrsp_valid_o = (state_q == ReturnResponse) && write_pending_q; + read_ready_o = state_q == ReadData; + write_o = '0; + write_o.data = write_data; + write_o.last = 1'b1; + for (int unsigned i = 0; i < HostDataBytes; i++) begin + write_o.strb[i] = (i >= byte_offset) && (i < byte_offset + operand_bytes); + end + write_valid_o = state_q == WriteData; + write_rsp_ready_o = state_q == WriteResponse; + end + + always_comb begin : proc_state + state_d = state_q; + request_d = request_q; + operand_d = operand_q; + read_data_d = read_data_q; + response_d = response_q; + read_pending_d = read_pending_q; + write_pending_d = write_pending_q; + + unique case (state_q) + Idle: begin + if (start_i) begin + request_d.addr = request_i.addr; + request_d.size = request_i.size; + request_d.atomic_op = request_i.atomic_op; + request_d.atomic_return = request_i.atomic_return; + response_d = request_valid_i ? hyperbus_pkg::HyperRespOkay : + hyperbus_pkg::HyperRespAtomicError; + state_d = WaitWriteData; + end + end + WaitWriteData: begin + if (host_w_valid_i && host_w_ready_o) begin + operand_d = host_w_i.data; + if (response_q == hyperbus_pkg::HyperRespOkay) begin + state_d = ReadCommand; + end else if (host_w_i.last) begin + read_pending_d = request_q.atomic_return; + write_pending_d = 1'b1; + state_d = ReturnResponse; + end + end + end + ReadCommand: if (command_valid_o && command_ready_i) state_d = ReadData; + ReadData: begin + if (read_valid_i && read_ready_o) begin + read_data_d = read_i.data; + if (read_i.resp == hyperbus_pkg::HyperRespOkay) begin + state_d = WriteCommand; + end else begin + response_d = read_i.resp; + read_pending_d = request_q.atomic_return; + write_pending_d = 1'b1; + state_d = ReturnResponse; + end + end + end + WriteCommand: if (command_valid_o && command_ready_i) state_d = WriteData; + WriteData: if (write_valid_o && write_ready_i) state_d = WriteResponse; + WriteResponse: begin + if (write_rsp_valid_i && write_rsp_ready_o) begin + response_d = write_rsp_error_i ? hyperbus_pkg::HyperRespAccessError : + hyperbus_pkg::HyperRespOkay; + read_pending_d = request_q.atomic_return; + write_pending_d = 1'b1; + state_d = ReturnResponse; + end + end + ReturnResponse: begin + if (host_r_valid_o && host_r_ready_i) read_pending_d = 1'b0; + if (host_wrsp_valid_o && host_wrsp_ready_i) write_pending_d = 1'b0; + if (completed_o) state_d = Idle; + end + default: state_d = Idle; + endcase + end + + `FFARN(state_q, state_d, Idle, clk_i, rst_ni) + `FFARN(request_q, request_d, '0, clk_i, rst_ni) + `FFARN(operand_q, operand_d, '0, clk_i, rst_ni) + `FFARN(read_data_q, read_data_d, '0, clk_i, rst_ni) + `FFARN(response_q, response_d, hyperbus_pkg::HyperRespOkay, clk_i, rst_ni) + `FFARN(read_pending_q, read_pending_d, 1'b0, clk_i, rst_ni) + `FFARN(write_pending_q, write_pending_d, 1'b0, clk_i, rst_ni) + +endmodule : hyperbus_atomic_handler diff --git a/src/hyperbus_axi.sv b/src/hyperbus_axi.sv index 84f0b5d..b45ee61 100644 --- a/src/hyperbus_axi.sv +++ b/src/hyperbus_axi.sv @@ -14,9 +14,7 @@ module hyperbus_axi_frontend #( parameter type axi_req_t = logic, parameter type axi_rsp_t = logic, parameter type host_req_t = logic, - parameter type host_w_t = logic, - parameter type host_r_t = logic, - parameter type host_wrsp_t = logic + parameter type host_rsp_t = logic ) ( input logic clk_i, input logic rst_ni, @@ -28,20 +26,7 @@ module hyperbus_axi_frontend #( output axi_rsp_t axi_rsp_o, output host_req_t host_req_o, - output logic host_req_valid_o, - input logic host_req_ready_i, - - output host_w_t host_w_o, - output logic host_w_valid_o, - input logic host_w_ready_i, - - input host_r_t host_r_i, - input logic host_r_valid_i, - output logic host_r_ready_o, - - input host_wrsp_t host_wrsp_i, - input logic host_wrsp_valid_i, - output logic host_wrsp_ready_o + input host_rsp_t host_rsp_i ); localparam int unsigned AxiDataBytes = AxiDataWidth / 8; @@ -117,9 +102,9 @@ module hyperbus_axi_frontend #( write_balance_t write_balance_d, write_balance_q; logic w_partial_d, w_partial_q; logic allow_aw, allow_w; - logic axi_ar_fire, axi_aw_fire, axi_w_fire; - logic axi_atomic_r_fire; - logic axi_r_last_fire, axi_b_fire; + logic axi_ar_accepted, axi_aw_accepted, axi_w_accepted; + logic axi_atomic_read_started; + logic axi_r_completed, axi_b_accepted; always_comb begin : proc_axi_drain allow_aw = !drain_i; @@ -142,13 +127,13 @@ module hyperbus_axi_frontend #( axi_rsp_o.w_ready = fifo_in_rsp.w_ready && allow_w; end - assign axi_ar_fire = axi_req_i.ar_valid && axi_rsp_o.ar_ready; - assign axi_aw_fire = axi_req_i.aw_valid && axi_rsp_o.aw_ready; - assign axi_w_fire = axi_req_i.w_valid && axi_rsp_o.w_ready; - assign axi_atomic_r_fire = axi_aw_fire && + assign axi_ar_accepted = axi_req_i.ar_valid && axi_rsp_o.ar_ready; + assign axi_aw_accepted = axi_req_i.aw_valid && axi_rsp_o.aw_ready; + assign axi_w_accepted = axi_req_i.w_valid && axi_rsp_o.w_ready; + assign axi_atomic_read_started = axi_aw_accepted && axi_req_i.aw.atop[axi_pkg::ATOP_R_RESP]; - assign axi_r_last_fire = axi_rsp_o.r_valid && axi_req_i.r_ready && axi_rsp_o.r.last; - assign axi_b_fire = axi_rsp_o.b_valid && axi_req_i.b_ready; + assign axi_r_completed = axi_rsp_o.r_valid && axi_req_i.r_ready && axi_rsp_o.r.last; + assign axi_b_accepted = axi_rsp_o.b_valid && axi_req_i.b_ready; always_comb begin : proc_pending_counts read_pending_d = read_pending_q; @@ -156,23 +141,23 @@ module hyperbus_axi_frontend #( write_balance_d = write_balance_q; w_partial_d = w_partial_q; - read_pending_d = read_pending_q + pending_cnt_t'(axi_ar_fire) + - pending_cnt_t'(axi_atomic_r_fire) - - pending_cnt_t'(axi_r_last_fire); + read_pending_d = read_pending_q + pending_cnt_t'(axi_ar_accepted) + + pending_cnt_t'(axi_atomic_read_started) - + pending_cnt_t'(axi_r_completed); - unique case ({axi_aw_fire, axi_b_fire}) + unique case ({axi_aw_accepted, axi_b_accepted}) 2'b10: write_pending_d = write_pending_q + 1'b1; 2'b01: write_pending_d = write_pending_q - 1'b1; default:; endcase - if (axi_aw_fire) begin + if (axi_aw_accepted) begin write_balance_d = write_balance_d + 1'b1; end - if (axi_w_fire && axi_req_i.w.last) begin + if (axi_w_accepted && axi_req_i.w.last) begin write_balance_d = write_balance_d - 1'b1; end - if (axi_w_fire) begin + if (axi_w_accepted) begin w_partial_d = !axi_req_i.w.last; end end @@ -262,54 +247,64 @@ module hyperbus_axi_frontend #( .valid_i ( spill_ax_valid ), .ready_o ( spill_ax_ready ), .data_i ( spill_ax_channel_in ), - .valid_o ( host_req_valid_o ), - .ready_i ( host_req_ready_i ), + .valid_o ( host_req_o.cmd_valid ), + .ready_i ( host_rsp_i.cmd_ready ), .data_o ( spill_ax_channel_out ) ); assign rr_out_req_ax = spill_ax_channel_out.ax_data; assign rr_out_req_write = spill_ax_channel_out.write; - assign host_req_o.write = rr_out_req_write; - assign host_req_o.addr = rr_out_req_ax.addr; - assign host_req_o.beats = hyperbus_pkg::hyper_blen_t'(rr_out_req_ax.len) + - hyperbus_pkg::hyper_blen_t'(1); - assign host_req_o.size = rr_out_req_ax.size; - assign host_req_o.burst = (rr_out_req_ax.burst == axi_pkg::BURST_FIXED) ? - hyperbus_pkg::HyperBurstFixed : - hyperbus_pkg::HyperBurstIncr; + assign host_req_o.cmd.write = rr_out_req_write; + assign host_req_o.cmd.addr = rr_out_req_ax.addr; + assign host_req_o.cmd.beats = hyperbus_pkg::hyper_blen_t'(rr_out_req_ax.len) + + hyperbus_pkg::hyper_blen_t'(1); + assign host_req_o.cmd.size = rr_out_req_ax.size; + assign host_req_o.cmd.burst = (rr_out_req_ax.burst == axi_pkg::BURST_FIXED) ? + hyperbus_pkg::HyperBurstFixed : + hyperbus_pkg::HyperBurstIncr; always_comb begin : proc_atomic_decode - host_req_o.atomic_op = (rr_out_req_ax.atop == '0) ? - hyperbus_pkg::HyperAtomicNone : - hyperbus_pkg::HyperAtomicInvalid; + host_req_o.cmd.atomic_op = (rr_out_req_ax.atop == '0) ? + hyperbus_pkg::HyperAtomicNone : + hyperbus_pkg::HyperAtomicInvalid; unique case (rr_out_req_ax.atop) - axi_pkg::ATOP_ATOMICSWAP: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicSwap; - axi_pkg::ATOP_ATOMICCMP: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicCompare; + axi_pkg::ATOP_ATOMICSWAP: + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicSwap; + axi_pkg::ATOP_ATOMICCMP: + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicCompare; default: begin if ((rr_out_req_ax.atop[5:4] == axi_pkg::ATOP_ATOMICSTORE) || (rr_out_req_ax.atop[5:4] == axi_pkg::ATOP_ATOMICLOAD)) begin unique case (rr_out_req_ax.atop[2:0]) - axi_pkg::ATOP_ADD: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicAdd; - axi_pkg::ATOP_CLR: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicClear; - axi_pkg::ATOP_EOR: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicXor; - axi_pkg::ATOP_SET: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicSet; - axi_pkg::ATOP_SMAX: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicSignedMax; - axi_pkg::ATOP_SMIN: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicSignedMin; - axi_pkg::ATOP_UMAX: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicUnsignedMax; - axi_pkg::ATOP_UMIN: host_req_o.atomic_op = hyperbus_pkg::HyperAtomicUnsignedMin; + axi_pkg::ATOP_ADD: + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicAdd; + axi_pkg::ATOP_CLR: + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicClear; + axi_pkg::ATOP_EOR: + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicXor; + axi_pkg::ATOP_SET: + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicSet; + axi_pkg::ATOP_SMAX: + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicSignedMax; + axi_pkg::ATOP_SMIN: + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicSignedMin; + axi_pkg::ATOP_UMAX: + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicUnsignedMax; + axi_pkg::ATOP_UMIN: + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicUnsignedMin; default:; endcase // AXI defines bit 3 as endianness for arithmetic atomics. if (rr_out_req_ax.atop[3] && ((rr_out_req_ax.atop[2:0] == axi_pkg::ATOP_ADD) || (rr_out_req_ax.atop[2:0] >= axi_pkg::ATOP_SMAX))) begin - host_req_o.atomic_op = hyperbus_pkg::HyperAtomicInvalid; + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicInvalid; end end end endcase - host_req_o.atomic_return = rr_out_req_ax.atop[axi_pkg::ATOP_R_RESP]; - host_req_o.ordered = rr_out_req_ax.atop != '0; + host_req_o.cmd.atomic_return = rr_out_req_ax.atop[axi_pkg::ATOP_R_RESP]; + host_req_o.cmd.ordered = rr_out_req_ax.atop != '0; end assign w_data_fifo_in.data = ser_out_req.w.data; @@ -335,57 +330,57 @@ module hyperbus_axi_frontend #( .ready_i ( w_data_ready ) ); - assign host_w_o.data = w_data_fifo.data; - assign host_w_o.strb = w_data_fifo.strb; - assign host_w_o.last = w_data_fifo.last; - assign host_w_valid_o = w_data_valid; - assign w_data_ready = host_w_ready_i; + assign host_req_o.w.data = w_data_fifo.data; + assign host_req_o.w.strb = w_data_fifo.strb; + assign host_req_o.w.last = w_data_fifo.last; + assign host_req_o.w_valid = w_data_valid; + assign w_data_ready = host_rsp_i.w_ready; - assign ser_out_rsp.r.data = host_r_i.data; - assign ser_out_rsp.r.last = host_r_i.last; + assign ser_out_rsp.r.data = host_rsp_i.r.data; + assign ser_out_rsp.r.last = host_rsp_i.r.last; always_comb begin : proc_host_resp ser_out_rsp.r.resp = axi_pkg::RESP_OKAY; ser_out_rsp.b.resp = axi_pkg::RESP_OKAY; - if (host_r_i.resp == hyperbus_pkg::HyperRespDecodeError) begin + if (host_rsp_i.r.resp == hyperbus_pkg::HyperRespDecodeError) begin ser_out_rsp.r.resp = axi_pkg::RESP_DECERR; - end else if (host_r_i.resp != hyperbus_pkg::HyperRespOkay) begin + end else if (host_rsp_i.r.resp != hyperbus_pkg::HyperRespOkay) begin ser_out_rsp.r.resp = axi_pkg::RESP_SLVERR; end - if (host_wrsp_i.resp == hyperbus_pkg::HyperRespDecodeError) begin + if (host_rsp_i.wrsp.resp == hyperbus_pkg::HyperRespDecodeError) begin ser_out_rsp.b.resp = axi_pkg::RESP_DECERR; - end else if (host_wrsp_i.resp != hyperbus_pkg::HyperRespOkay) begin + end else if (host_rsp_i.wrsp.resp != hyperbus_pkg::HyperRespOkay) begin ser_out_rsp.b.resp = axi_pkg::RESP_SLVERR; end end assign ser_out_rsp.r.id = '0; assign ser_out_rsp.r.user = '0; - assign ser_out_rsp.r_valid = host_r_valid_i; - assign host_r_ready_o = ser_out_req.r_ready; + assign ser_out_rsp.r_valid = host_rsp_i.r_valid; + assign host_req_o.r_ready = ser_out_req.r_ready; assign ser_out_rsp.b.user = '0; assign ser_out_rsp.b.id = '0; - assign ser_out_rsp.b_valid = host_wrsp_valid_i; - assign host_wrsp_ready_o = ser_out_req.b_ready; + assign ser_out_rsp.b_valid = host_rsp_i.wrsp_valid; + assign host_req_o.wrsp_ready = ser_out_req.b_ready; // pragma translate_off `ifndef VERILATOR axi_burst_type : assert property( - @(posedge clk_i) host_req_valid_o && host_req_ready_i |-> + @(posedge clk_i) host_req_o.cmd_valid && host_rsp_i.cmd_ready |-> ((rr_out_req_ax.burst == axi_pkg::BURST_INCR) || ((rr_out_req_ax.burst == axi_pkg::BURST_FIXED) && (rr_out_req_ax.len == '0)))) else $fatal(1, "Only incremental bursts and single-beat fixed bursts are supported."); read_response_pending : assert property( @(posedge clk_i) disable iff (!rst_ni) - axi_r_last_fire |-> (read_pending_q != '0)) + axi_r_completed |-> (read_pending_q != '0)) else $fatal(1, "AXI read response completed without a pending request."); write_response_pending : assert property( @(posedge clk_i) disable iff (!rst_ni) - axi_b_fire |-> (write_pending_q != '0)) + axi_b_accepted |-> (write_pending_q != '0)) else $fatal(1, "AXI write response completed without a pending request."); `endif // pragma translate_on diff --git a/src/hyperbus_backend.sv b/src/hyperbus_backend.sv index 341d040..7d40185 100644 --- a/src/hyperbus_backend.sv +++ b/src/hyperbus_backend.sv @@ -12,7 +12,8 @@ module hyperbus_backend #( parameter int unsigned SyncStages = 2, parameter type hyper_rx_t = logic, parameter type hyper_tx_t = logic, - parameter type hyper_cmd_t = logic + parameter type hyper_req_t = logic, + parameter type hyper_rsp_t = logic ) ( input logic clk_i, input logic clk_90_i, @@ -26,18 +27,8 @@ module hyperbus_backend #( output logic busy_o, output logic [7:0] tx_clk_delay_o, - output hyper_rx_t rx_o, - output logic rx_valid_o, - input logic rx_ready_i, - input hyper_tx_t tx_i, - input logic tx_valid_i, - output logic tx_ready_o, - output logic wrsp_error_o, - output logic wrsp_valid_o, - input logic wrsp_ready_i, - input hyper_cmd_t cmd_i, - input logic cmd_valid_i, - output logic cmd_ready_o, + input hyper_req_t req_i, + output hyper_rsp_t rsp_o, output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, output logic [NumPhys-1:0] hyper_ck_o, @@ -51,59 +42,202 @@ module hyperbus_backend #( output logic [NumPhys-1:0] hyper_reset_no ); - hyperbus_pkg::phy_cfg_t cfg_q, cfg_d; - logic phy_busy; - logic cfg_apply_fire; + hyperbus_pkg::phy_cfg_t cfg_q; + logic cfg_apply_accepted; + logic phy_busy_any; `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= 8) `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) `ASSERT_INIT(SyncStagesValid, SyncStages >= 2) - assign cfg_apply_ready_o = ~phy_busy; - assign cfg_apply_fire = cfg_apply_valid_i & cfg_apply_ready_o; - assign cfg_d = cfg_apply_fire ? cfg_apply_i : cfg_q; - assign busy_o = phy_busy | cfg_apply_valid_i; - assign tx_clk_delay_o = cfg_q.t_tx_clk_delay; - - `FFARN(cfg_q, cfg_d, '0, clk_i, rst_ni) - - hyperbus_phy_if #( - .NumChips ( NumChips ), - .NumPhys ( NumPhys ), - .StartupCycles ( StartupCycles ), - .hyper_rx_t ( hyper_rx_t ), - .hyper_tx_t ( hyper_tx_t ), - .SyncStages ( SyncStages ) - ) i_phy ( - .clk_phy_i ( clk_i ), - .clk_phy_i_90 ( clk_90_i ), - .rst_phy_ni ( rst_ni ), - .test_mode_i ( test_mode_i ), - .cfg_i ( cfg_q ), - .busy_o ( phy_busy ), - .rx_o ( rx_o ), - .rx_valid_o ( rx_valid_o ), - .rx_ready_i ( rx_ready_i ), - .tx_i ( tx_i ), - .tx_valid_i ( tx_valid_i ), - .tx_ready_o ( tx_ready_o ), - .b_error_o ( wrsp_error_o ), - .b_valid_o ( wrsp_valid_o ), - .b_ready_i ( wrsp_ready_i ), - .trans_i ( cmd_i.trans ), - .trans_cs_i ( cmd_i.cs ), - .trans_valid_i ( cmd_valid_i ), - .trans_ready_o ( cmd_ready_o ), - .hyper_cs_no ( hyper_cs_no ), - .hyper_ck_o ( hyper_ck_o ), - .hyper_ck_no ( hyper_ck_no ), - .hyper_rwds_o ( hyper_rwds_o ), - .hyper_rwds_i ( hyper_rwds_i ), - .hyper_rwds_oe_o ( hyper_rwds_oe_o ), - .hyper_dq_i ( hyper_dq_i ), - .hyper_dq_o ( hyper_dq_o ), - .hyper_dq_oe_o ( hyper_dq_oe_o ), - .hyper_reset_no ( hyper_reset_no ) - ); + assign cfg_apply_ready_o = ~phy_busy_any; + assign cfg_apply_accepted = cfg_apply_valid_i && cfg_apply_ready_o; + assign busy_o = phy_busy_any || cfg_apply_valid_i; + assign tx_clk_delay_o = cfg_q.t_tx_clk_delay; + + `FFLARN(cfg_q, cfg_apply_i, cfg_apply_accepted, '0, clk_i, rst_ni) + + if (NumPhys == 2) begin : gen_dual_phy + hyperbus_pkg::phy_rx_t [NumPhys-1:0] phy_rx; + hyperbus_pkg::phy_rx_t [NumPhys-1:0] buffered_rx; + logic [NumPhys-1:0] phy_rx_valid; + logic [NumPhys-1:0] phy_rx_ready; + logic [NumPhys-1:0] buffered_rx_valid; + logic [NumPhys-1:0][1:0] buffered_rx_usage; + logic [NumPhys-1:0] phy_tx_ready; + logic [NumPhys-1:0] phy_tx_valid; + logic [NumPhys-1:0] phy_cmd_ready; + logic [NumPhys-1:0] phy_cmd_valid; + logic [NumPhys-1:0] phy_wrsp_valid; + logic [NumPhys-1:0] phy_wrsp_error; + logic [NumPhys-1:0] phy_wrsp_ready; + logic [NumPhys-1:0] phy_enable; + logic [NumPhys-1:0] phy_busy; + logic [NumPhys-1:0] phy_active_q; + logic [NumPhys-1:0] tx_fork_valid; + logic [NumPhys-1:0] cmd_fork_valid; + logic [NumPhys-1:0] rx_join_ready; + logic active_change; + + assign phy_enable = cfg_q.dual_phy ? '1 : 2'b01; + assign active_change = phy_active_q != phy_enable; + `FFLARN(phy_active_q, phy_enable | phy_busy, + active_change && (buffered_rx_usage == '0), '1, clk_i, rst_ni) + + assign phy_busy_any = |phy_busy; + assign rsp_o.rx.error = |({buffered_rx[1].error, buffered_rx[0].error} & + phy_active_q); + assign rsp_o.rx.last = &({buffered_rx[1].last, buffered_rx[0].last} | + ~phy_active_q); + assign rsp_o.wrsp.error = |(phy_wrsp_error & phy_active_q); + + stream_fork #( + .N_OUP ( NumPhys ) + ) i_tx_fork ( + .clk_i, + .rst_ni, + .valid_i ( req_i.tx_valid ), + .ready_o ( rsp_o.tx_ready ), + .valid_o ( tx_fork_valid ), + .ready_i ( phy_tx_ready | ~phy_active_q ) + ); + + assign phy_tx_valid = tx_fork_valid & phy_active_q; + + stream_fork #( + .N_OUP ( NumPhys ) + ) i_cmd_fork ( + .clk_i, + .rst_ni, + .valid_i ( req_i.cmd_valid && !active_change ), + .ready_o ( rsp_o.cmd_ready ), + .valid_o ( cmd_fork_valid ), + .ready_i ( phy_cmd_ready | ~phy_active_q ) + ); + + assign phy_cmd_valid = cmd_fork_valid & phy_active_q; + + stream_join #( + .N_INP ( NumPhys ) + ) i_rx_join ( + .inp_valid_i ( buffered_rx_valid | ~phy_active_q ), + .inp_ready_o ( rx_join_ready ), + .oup_valid_o ( rsp_o.rx_valid ), + .oup_ready_i ( req_i.rx_ready ) + ); + + stream_join #( + .N_INP ( NumPhys ) + ) i_wrsp_join ( + .inp_valid_i ( phy_wrsp_valid | ~phy_active_q ), + .inp_ready_o ( phy_wrsp_ready ), + .oup_valid_o ( rsp_o.wrsp_valid ), + .oup_ready_i ( req_i.wrsp_ready ) + ); + + for (genvar i = 0; i < NumPhys; i++) begin : gen_phy + assign rsp_o.rx.data[i*16 +: 16] = buffered_rx[i].data; + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( 4 ), + .T ( hyperbus_pkg::phy_rx_t ) + ) i_rx_fifo ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( buffered_rx_usage[i] ), + .data_i ( phy_rx[i] ), + .valid_i ( phy_rx_valid[i] ), + .ready_o ( phy_rx_ready[i] ), + .data_o ( buffered_rx[i] ), + .valid_o ( buffered_rx_valid[i] ), + .ready_i ( rx_join_ready[i] ) + ); + + hyperbus_phy #( + .NumChips ( NumChips ), + .StartupCycles ( StartupCycles ), + .NumPhys ( NumPhys ), + .SyncStages ( SyncStages ) + ) i_phy ( + .clk_i, + .clk_i_90 ( clk_90_i ), + .rst_ni, + .test_mode_i, + .cfg_i ( cfg_q ), + .busy_o ( phy_busy[i] ), + .rx_data_o ( phy_rx[i].data ), + .rx_last_o ( phy_rx[i].last ), + .rx_error_o ( phy_rx[i].error ), + .rx_valid_o ( phy_rx_valid[i] ), + .rx_ready_i ( phy_rx_ready[i] ), + .tx_data_i ( req_i.tx.data[16*i +: 16] ), + .tx_strb_i ( req_i.tx.strb[2*i +: 2] ), + .tx_last_i ( req_i.tx.last ), + .tx_valid_i ( phy_tx_valid[i] ), + .tx_ready_o ( phy_tx_ready[i] ), + .b_error_o ( phy_wrsp_error[i] ), + .b_valid_o ( phy_wrsp_valid[i] ), + .b_ready_i ( phy_wrsp_ready[i] ), + .trans_i ( req_i.cmd.trans ), + .trans_cs_i ( req_i.cmd.cs ), + .trans_valid_i ( phy_cmd_valid[i] ), + .trans_ready_o ( phy_cmd_ready[i] ), + .hyper_cs_no ( hyper_cs_no[i] ), + .hyper_ck_o ( hyper_ck_o[i] ), + .hyper_ck_no ( hyper_ck_no[i] ), + .hyper_rwds_o ( hyper_rwds_o[i] ), + .hyper_rwds_i ( hyper_rwds_i[i] ), + .hyper_rwds_oe_o( hyper_rwds_oe_o[i] ), + .hyper_dq_i ( hyper_dq_i[i] ), + .hyper_dq_o ( hyper_dq_o[i] ), + .hyper_dq_oe_o ( hyper_dq_oe_o[i] ), + .hyper_reset_no ( hyper_reset_no[i] ) + ); + end + end else begin : gen_single_phy + hyperbus_phy #( + .NumChips ( NumChips ), + .StartupCycles ( StartupCycles ), + .NumPhys ( NumPhys ), + .SyncStages ( SyncStages ) + ) i_phy ( + .clk_i, + .clk_i_90 ( clk_90_i ), + .rst_ni, + .test_mode_i, + .cfg_i ( cfg_q ), + .busy_o ( phy_busy_any ), + .rx_data_o ( rsp_o.rx.data ), + .rx_last_o ( rsp_o.rx.last ), + .rx_error_o ( rsp_o.rx.error ), + .rx_valid_o ( rsp_o.rx_valid ), + .rx_ready_i ( req_i.rx_ready ), + .tx_data_i ( req_i.tx.data ), + .tx_strb_i ( req_i.tx.strb ), + .tx_last_i ( req_i.tx.last ), + .tx_valid_i ( req_i.tx_valid ), + .tx_ready_o ( rsp_o.tx_ready ), + .b_error_o ( rsp_o.wrsp.error ), + .b_valid_o ( rsp_o.wrsp_valid ), + .b_ready_i ( req_i.wrsp_ready ), + .trans_i ( req_i.cmd.trans ), + .trans_cs_i ( req_i.cmd.cs ), + .trans_valid_i ( req_i.cmd_valid ), + .trans_ready_o ( rsp_o.cmd_ready ), + .hyper_cs_no ( hyper_cs_no ), + .hyper_ck_o ( hyper_ck_o ), + .hyper_ck_no ( hyper_ck_no ), + .hyper_rwds_o ( hyper_rwds_o ), + .hyper_rwds_i ( hyper_rwds_i ), + .hyper_rwds_oe_o( hyper_rwds_oe_o ), + .hyper_dq_i ( hyper_dq_i ), + .hyper_dq_o ( hyper_dq_o ), + .hyper_dq_oe_o ( hyper_dq_oe_o ), + .hyper_reset_no ( hyper_reset_no ) + ); + end endmodule diff --git a/src/hyperbus_iso_bridge.sv b/src/hyperbus_iso_bridge.sv index d351b2f..91db8fc 100644 --- a/src/hyperbus_iso_bridge.sv +++ b/src/hyperbus_iso_bridge.sv @@ -9,7 +9,10 @@ module hyperbus_iso_bridge #( parameter int unsigned TxFifoLogDepth = 3, parameter type hyper_rx_t = logic, parameter type hyper_tx_t = logic, - parameter type hyper_cmd_t = logic + parameter type hyper_wrsp_t = logic, + parameter type hyper_cmd_t = logic, + parameter type hyper_req_t = logic, + parameter type hyper_rsp_t = logic ) ( input logic clk_sys_i, input logic rst_sys_ni, @@ -21,31 +24,10 @@ module hyperbus_iso_bridge #( output logic frontend_cfg_apply_ready_o, output logic frontend_cfg_apply_done_o, - output hyper_rx_t frontend_rx_o, - output logic frontend_rx_valid_o, - input logic frontend_rx_ready_i, - input hyper_tx_t frontend_tx_i, - input logic frontend_tx_valid_i, - output logic frontend_tx_ready_o, - output logic frontend_wrsp_error_o, - output logic frontend_wrsp_valid_o, - input logic frontend_wrsp_ready_i, - input hyper_cmd_t frontend_cmd_i, - input logic frontend_cmd_valid_i, - output logic frontend_cmd_ready_o, - - input hyper_rx_t backend_rx_i, - input logic backend_rx_valid_i, - output logic backend_rx_ready_o, - output hyper_tx_t backend_tx_o, - output logic backend_tx_valid_o, - input logic backend_tx_ready_i, - input logic backend_wrsp_error_i, - input logic backend_wrsp_valid_i, - output logic backend_wrsp_ready_o, - output hyper_cmd_t backend_cmd_o, - output logic backend_cmd_valid_o, - input logic backend_cmd_ready_i, + input hyper_req_t frontend_req_i, + output hyper_rsp_t frontend_rsp_o, + output hyper_req_t backend_req_o, + input hyper_rsp_t backend_rsp_i, output hyperbus_pkg::phy_cfg_t cfg_apply_o, output logic cfg_apply_valid_o, @@ -53,13 +35,13 @@ module hyperbus_iso_bridge #( ); hyperbus_pkg::phy_cfg_t cfg_apply_data_q; - logic cfg_apply_src_fire; + logic cfg_apply_accepted; logic cfg_apply_pending_d; logic cfg_apply_pending_q; hyper_cmd_t cmd_data_q; - logic cmd_src_fire; - logic wrsp_error_q; - logic wrsp_src_fire; + logic cmd_accepted; + hyper_wrsp_t wrsp_data_q; + logic wrsp_accepted; hyper_tx_t tx_fifo_data; logic tx_fifo_valid; logic tx_fifo_ready; @@ -73,36 +55,36 @@ module hyperbus_iso_bridge #( logic rx_fifo_valid; logic rx_fifo_ready; - assign cfg_apply_src_fire = frontend_cfg_apply_valid_i && frontend_cfg_apply_ready_o; + assign cfg_apply_accepted = frontend_cfg_apply_valid_i && frontend_cfg_apply_ready_o; assign frontend_cfg_apply_done_o = cfg_apply_pending_q && frontend_cfg_apply_ready_o; - assign cmd_src_fire = frontend_cmd_valid_i && frontend_cmd_ready_o; - assign wrsp_src_fire = backend_wrsp_valid_i && backend_wrsp_ready_o; + assign cmd_accepted = frontend_req_i.cmd_valid && frontend_rsp_o.cmd_ready; + assign wrsp_accepted = backend_rsp_i.wrsp_valid && backend_req_o.wrsp_ready; - always_comb begin + always_comb begin : proc_cfg_apply_pending cfg_apply_pending_d = cfg_apply_pending_q; - if (cfg_apply_src_fire) begin + if (cfg_apply_accepted) begin cfg_apply_pending_d = 1'b1; end else if (frontend_cfg_apply_done_o) begin cfg_apply_pending_d = 1'b0; end end - `FFLARN(cfg_apply_data_q, cfg_apply_i, cfg_apply_src_fire, '0, clk_sys_i, rst_sys_ni) + `FFLARN(cfg_apply_data_q, cfg_apply_i, cfg_apply_accepted, '0, clk_sys_i, rst_sys_ni) `FFARN(cfg_apply_pending_q, cfg_apply_pending_d, 1'b0, clk_sys_i, rst_sys_ni) - `FFLARN(cmd_data_q, frontend_cmd_i, cmd_src_fire, '0, clk_sys_i, rst_sys_ni) - `FFLARN(wrsp_error_q, backend_wrsp_error_i, wrsp_src_fire, 1'b0, clk_phy_i, rst_phy_ni) + `FFLARN(cmd_data_q, frontend_req_i.cmd, cmd_accepted, '0, clk_sys_i, rst_sys_ni) + `FFLARN(wrsp_data_q, backend_rsp_i.wrsp, wrsp_accepted, '0, clk_phy_i, rst_phy_ni) isochronous_4phase_handshake i_iso_cfg_apply ( - .src_clk_i ( clk_sys_i ), - .src_rst_ni ( rst_sys_ni ), - .src_valid_i ( frontend_cfg_apply_valid_i ), - .src_ready_o ( frontend_cfg_apply_ready_o ), - .dst_clk_i ( clk_phy_i ), - .dst_rst_ni ( rst_phy_ni ), - .dst_valid_o ( cfg_apply_valid_o ), - .dst_ready_i ( cfg_apply_ready_i ) + .src_clk_i ( clk_sys_i ), + .src_rst_ni ( rst_sys_ni ), + .src_valid_i ( frontend_cfg_apply_valid_i ), + .src_ready_o ( frontend_cfg_apply_ready_o ), + .dst_clk_i ( clk_phy_i ), + .dst_rst_ni ( rst_phy_ni ), + .dst_valid_o ( cfg_apply_valid_o ), + .dst_ready_i ( cfg_apply_ready_i ) ); assign cfg_apply_o = cfg_apply_data_q; @@ -110,45 +92,45 @@ module hyperbus_iso_bridge #( isochronous_4phase_handshake i_iso_cmd ( .src_clk_i ( clk_sys_i ), .src_rst_ni ( rst_sys_ni ), - .src_valid_i ( frontend_cmd_valid_i ), - .src_ready_o ( frontend_cmd_ready_o ), + .src_valid_i ( frontend_req_i.cmd_valid ), + .src_ready_o ( frontend_rsp_o.cmd_ready ), .dst_clk_i ( clk_phy_i ), .dst_rst_ni ( rst_phy_ni ), - .dst_valid_o ( backend_cmd_valid_o ), - .dst_ready_i ( backend_cmd_ready_i ) + .dst_valid_o ( backend_req_o.cmd_valid ), + .dst_ready_i ( backend_rsp_i.cmd_ready ) ); - assign backend_cmd_o = cmd_data_q; + assign backend_req_o.cmd = cmd_data_q; isochronous_4phase_handshake i_iso_wrsp ( - .src_clk_i ( clk_phy_i ), - .src_rst_ni ( rst_phy_ni ), - .src_valid_i ( backend_wrsp_valid_i ), - .src_ready_o ( backend_wrsp_ready_o ), - .dst_clk_i ( clk_sys_i ), - .dst_rst_ni ( rst_sys_ni ), - .dst_valid_o ( frontend_wrsp_valid_o ), - .dst_ready_i ( frontend_wrsp_ready_i ) + .src_clk_i ( clk_phy_i ), + .src_rst_ni ( rst_phy_ni ), + .src_valid_i ( backend_rsp_i.wrsp_valid ), + .src_ready_o ( backend_req_o.wrsp_ready ), + .dst_clk_i ( clk_sys_i ), + .dst_rst_ni ( rst_sys_ni ), + .dst_valid_o ( frontend_rsp_o.wrsp_valid ), + .dst_ready_i ( frontend_req_i.wrsp_ready ) ); - assign frontend_wrsp_error_o = wrsp_error_q; + assign frontend_rsp_o.wrsp = wrsp_data_q; stream_fifo #( .FALL_THROUGH ( 1'b0 ), .DEPTH ( 1 << TxFifoLogDepth ), .T ( hyper_tx_t ) ) i_tx_fifo ( - .clk_i ( clk_sys_i ), - .rst_ni ( rst_sys_ni ), - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o ( ), - .data_i ( frontend_tx_i ), - .valid_i ( frontend_tx_valid_i ), - .ready_o ( frontend_tx_ready_o ), - .data_o ( tx_fifo_data ), - .valid_o ( tx_fifo_valid ), - .ready_i ( tx_fifo_ready ) + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( ), + .data_i ( frontend_req_i.tx ), + .valid_i ( frontend_req_i.tx_valid ), + .ready_o ( frontend_rsp_o.tx_ready ), + .data_o ( tx_fifo_data ), + .valid_o ( tx_fifo_valid ), + .ready_i ( tx_fifo_ready ) ); isochronous_spill_register #( @@ -161,9 +143,9 @@ module hyperbus_iso_bridge #( .src_data_i ( tx_fifo_data ), .dst_clk_i ( clk_phy_i ), .dst_rst_ni ( rst_phy_ni ), - .dst_valid_o ( backend_tx_valid_o ), - .dst_ready_i ( backend_tx_ready_i ), - .dst_data_o ( backend_tx_o ) + .dst_valid_o ( backend_req_o.tx_valid ), + .dst_ready_i ( backend_rsp_i.tx_ready ), + .dst_data_o ( backend_req_o.tx ) ); stream_fifo #( @@ -171,17 +153,17 @@ module hyperbus_iso_bridge #( .DEPTH ( 1 << RxFifoLogDepth ), .T ( hyper_rx_t ) ) i_rx_src_fifo ( - .clk_i ( clk_phy_i ), - .rst_ni ( rst_phy_ni ), - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o ( ), - .data_i ( backend_rx_i ), - .valid_i ( backend_rx_valid_i ), - .ready_o ( backend_rx_ready_o ), - .data_o ( rx_src_fifo_data ), - .valid_o ( rx_src_fifo_valid ), - .ready_i ( rx_src_fifo_ready ) + .clk_i ( clk_phy_i ), + .rst_ni ( rst_phy_ni ), + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( ), + .data_i ( backend_rsp_i.rx ), + .valid_i ( backend_rsp_i.rx_valid ), + .ready_o ( backend_req_o.rx_ready ), + .data_o ( rx_src_fifo_data ), + .valid_o ( rx_src_fifo_valid ), + .ready_i ( rx_src_fifo_ready ) ); isochronous_spill_register #( @@ -217,8 +199,8 @@ module hyperbus_iso_bridge #( .ready_i ( rx_fifo_ready ) ); - assign frontend_rx_o = rx_fifo_data; - assign frontend_rx_valid_o = rx_fifo_valid; - assign rx_fifo_ready = frontend_rx_ready_i; + assign frontend_rsp_o.rx = rx_fifo_data; + assign frontend_rsp_o.rx_valid = rx_fifo_valid; + assign rx_fifo_ready = frontend_req_i.rx_ready; endmodule diff --git a/src/hyperbus_isochronous.sv b/src/hyperbus_isochronous.sv index 11b0f05..2be9c01 100644 --- a/src/hyperbus_isochronous.sv +++ b/src/hyperbus_isochronous.sv @@ -50,27 +50,11 @@ module hyperbus_isochronous #( `ASSERT_INIT(AxiAddrWidthValid, AxiAddrWidth >= $clog2(AxiDataWidth / 8)) - typedef struct packed { - logic [(16*NumPhys)-1:0] data; - logic last; - logic [(2*NumPhys)-1:0] strb; - } hyper_tx_t; - - typedef struct packed { - logic [(16*NumPhys)-1:0] data; - logic last; - logic error; - } hyper_rx_t; - - typedef struct packed { - hyperbus_pkg::hyper_tf_t trans; - logic [NumChips-1:0] cs; - } hyper_cmd_t; - typedef logic [AxiAddrWidth-1:0] host_addr_t; typedef logic [AxiDataWidth-1:0] host_data_t; typedef logic [AxiDataWidth/8-1:0] host_strb_t; `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) + `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys, NumChips) logic clk_backend; logic clk_backend_90; @@ -90,45 +74,13 @@ module hyperbus_isochronous #( axi_rule_t [NumChips-1:0] frontend_chip_rules; host_req_t host_req; - logic host_req_valid; - logic host_req_ready; - host_w_t host_w; - logic host_w_valid; - logic host_w_ready; - host_r_t host_r; - logic host_r_valid; - logic host_r_ready; - host_wrsp_t host_wrsp; - logic host_wrsp_valid; - logic host_wrsp_ready; - - hyper_rx_t midend_rx; - logic midend_rx_valid; - logic midend_rx_ready; - hyper_tx_t midend_tx; - logic midend_tx_valid; - logic midend_tx_ready; - logic midend_wrsp_error; - logic midend_wrsp_valid; - logic midend_wrsp_ready; - hyper_cmd_t midend_cmd; - logic midend_cmd_valid; - logic midend_cmd_ready; + host_rsp_t host_rsp; + hyper_req_t midend_req; + hyper_rsp_t midend_rsp; logic midend_trans_active; logic midend_decode_error; - - hyper_rx_t backend_rx; - logic backend_rx_valid; - logic backend_rx_ready; - hyper_tx_t backend_tx; - logic backend_tx_valid; - logic backend_tx_ready; - logic backend_wrsp_error; - logic backend_wrsp_valid; - logic backend_wrsp_ready; - hyper_cmd_t backend_cmd; - logic backend_cmd_valid; - logic backend_cmd_ready; + hyper_req_t backend_req; + hyper_rsp_t backend_rsp; hyperbus_clk_gen i_clk_gen ( .clk_i ( clk_sys_i ), @@ -180,9 +132,7 @@ module hyperbus_isochronous #( .axi_req_t ( axi_req_t ), .axi_rsp_t ( axi_rsp_t ), .host_req_t ( host_req_t ), - .host_w_t ( host_w_t ), - .host_r_t ( host_r_t ), - .host_wrsp_t ( host_wrsp_t ) + .host_rsp_t ( host_rsp_t ) ) i_axi_frontend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), @@ -190,18 +140,8 @@ module hyperbus_isochronous #( .idle_o ( host_idle ), .axi_req_i ( axi_req_i ), .axi_rsp_o ( axi_rsp_o ), - .host_req_o ( host_req ), - .host_req_valid_o ( host_req_valid ), - .host_req_ready_i ( host_req_ready ), - .host_w_o ( host_w ), - .host_w_valid_o ( host_w_valid ), - .host_w_ready_i ( host_w_ready ), - .host_r_i ( host_r ), - .host_r_valid_i ( host_r_valid ), - .host_r_ready_o ( host_r_ready ), - .host_wrsp_i ( host_wrsp ), - .host_wrsp_valid_i ( host_wrsp_valid ), - .host_wrsp_ready_o ( host_wrsp_ready ) + .host_req_o ( host_req ), + .host_rsp_i ( host_rsp ) ); hyperbus_midend #( @@ -209,45 +149,29 @@ module hyperbus_isochronous #( .HostDataWidth ( AxiDataWidth ), .NumChips ( NumChips ), .NumPhys ( NumPhys ), - .host_req_t ( host_req_t ), + .host_cmd_t ( host_cmd_t ), .host_w_t ( host_w_t ), .host_r_t ( host_r_t ), .host_wrsp_t ( host_wrsp_t ), + .host_req_t ( host_req_t ), + .host_rsp_t ( host_rsp_t ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), .hyper_cmd_t ( hyper_cmd_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ), .rule_t ( axi_rule_t ) ) i_midend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), - .host_req_i ( host_req ), - .host_req_valid_i ( host_req_valid ), - .host_req_ready_o ( host_req_ready ), - .host_w_i ( host_w ), - .host_w_valid_i ( host_w_valid ), - .host_w_ready_o ( host_w_ready ), - .host_r_o ( host_r ), - .host_r_valid_o ( host_r_valid ), - .host_r_ready_i ( host_r_ready ), - .host_wrsp_o ( host_wrsp ), - .host_wrsp_valid_o ( host_wrsp_valid ), - .host_wrsp_ready_i ( host_wrsp_ready ), + .host_link_req_i ( host_req ), + .host_link_rsp_o ( host_rsp ), .frontend_cfg_i ( frontend_cfg ), .chip_rules_i ( frontend_chip_rules ), .trans_active_o ( midend_trans_active ), .decode_error_o ( midend_decode_error ), - .rx_i ( midend_rx ), - .rx_valid_i ( midend_rx_valid ), - .rx_ready_o ( midend_rx_ready ), - .tx_o ( midend_tx ), - .tx_valid_o ( midend_tx_valid ), - .tx_ready_i ( midend_tx_ready ), - .wrsp_error_i ( midend_wrsp_error ), - .wrsp_valid_i ( midend_wrsp_valid ), - .wrsp_ready_o ( midend_wrsp_ready ), - .cmd_o ( midend_cmd ), - .cmd_valid_o ( midend_cmd_valid ), - .cmd_ready_i ( midend_cmd_ready ) + .hyper_link_req_o ( midend_req ), + .hyper_link_rsp_i ( midend_rsp ) ); hyperbus_iso_bridge #( @@ -255,7 +179,10 @@ module hyperbus_isochronous #( .TxFifoLogDepth ( TxFifoLogDepth ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), - .hyper_cmd_t ( hyper_cmd_t ) + .hyper_wrsp_t ( hyper_wrsp_t ), + .hyper_cmd_t ( hyper_cmd_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ) ) i_bridge ( .clk_sys_i ( clk_sys_i ), .rst_sys_ni ( rst_sys_ni ), @@ -265,30 +192,10 @@ module hyperbus_isochronous #( .frontend_cfg_apply_valid_i ( frontend_cfg_apply_valid ), .frontend_cfg_apply_ready_o ( frontend_cfg_apply_ready ), .frontend_cfg_apply_done_o ( frontend_cfg_apply_done ), - .frontend_rx_o ( midend_rx ), - .frontend_rx_valid_o ( midend_rx_valid ), - .frontend_rx_ready_i ( midend_rx_ready ), - .frontend_tx_i ( midend_tx ), - .frontend_tx_valid_i ( midend_tx_valid ), - .frontend_tx_ready_o ( midend_tx_ready ), - .frontend_wrsp_error_o ( midend_wrsp_error ), - .frontend_wrsp_valid_o ( midend_wrsp_valid ), - .frontend_wrsp_ready_i ( midend_wrsp_ready ), - .frontend_cmd_i ( midend_cmd ), - .frontend_cmd_valid_i ( midend_cmd_valid ), - .frontend_cmd_ready_o ( midend_cmd_ready ), - .backend_rx_i ( backend_rx ), - .backend_rx_valid_i ( backend_rx_valid ), - .backend_rx_ready_o ( backend_rx_ready ), - .backend_tx_o ( backend_tx ), - .backend_tx_valid_o ( backend_tx_valid ), - .backend_tx_ready_i ( backend_tx_ready ), - .backend_wrsp_error_i ( backend_wrsp_error ), - .backend_wrsp_valid_i ( backend_wrsp_valid ), - .backend_wrsp_ready_o ( backend_wrsp_ready ), - .backend_cmd_o ( backend_cmd ), - .backend_cmd_valid_o ( backend_cmd_valid ), - .backend_cmd_ready_i ( backend_cmd_ready ), + .frontend_req_i ( midend_req ), + .frontend_rsp_o ( midend_rsp ), + .backend_req_o ( backend_req ), + .backend_rsp_i ( backend_rsp ), .cfg_apply_o ( backend_cfg_apply ), .cfg_apply_valid_o ( backend_cfg_apply_valid ), .cfg_apply_ready_i ( backend_cfg_apply_ready ) @@ -301,7 +208,8 @@ module hyperbus_isochronous #( .SyncStages ( SyncStages ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), - .hyper_cmd_t ( hyper_cmd_t ) + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ) ) i_backend ( .clk_i ( clk_backend ), .clk_90_i ( clk_backend_90 ), @@ -312,18 +220,8 @@ module hyperbus_isochronous #( .cfg_apply_ready_o ( backend_cfg_apply_ready ), .busy_o ( ), .tx_clk_delay_o ( ), - .rx_o ( backend_rx ), - .rx_valid_o ( backend_rx_valid ), - .rx_ready_i ( backend_rx_ready ), - .tx_i ( backend_tx ), - .tx_valid_i ( backend_tx_valid ), - .tx_ready_o ( backend_tx_ready ), - .wrsp_error_o ( backend_wrsp_error ), - .wrsp_valid_o ( backend_wrsp_valid ), - .wrsp_ready_i ( backend_wrsp_ready ), - .cmd_i ( backend_cmd ), - .cmd_valid_i ( backend_cmd_valid ), - .cmd_ready_o ( backend_cmd_ready ), + .req_i ( backend_req ), + .rsp_o ( backend_rsp ), .hyper_cs_no ( hyper_cs_no ), .hyper_ck_o ( hyper_ck_o ), .hyper_ck_no ( hyper_ck_no ), diff --git a/src/hyperbus_midend.sv b/src/hyperbus_midend.sv index c0fb55f..73f78cd 100644 --- a/src/hyperbus_midend.sv +++ b/src/hyperbus_midend.sv @@ -10,60 +10,37 @@ module hyperbus_midend #( parameter int unsigned HostDataWidth = -1, parameter int unsigned NumChips = -1, parameter int unsigned NumPhys = -1, - parameter type host_req_t = logic, + parameter type host_cmd_t = logic, parameter type host_w_t = logic, parameter type host_r_t = logic, parameter type host_wrsp_t = logic, + parameter type host_req_t = logic, + parameter type host_rsp_t = logic, parameter type hyper_rx_t = logic, parameter type hyper_tx_t = logic, parameter type hyper_cmd_t = logic, + parameter type hyper_req_t = logic, + parameter type hyper_rsp_t = logic, parameter type rule_t = logic ) ( input logic clk_i, input logic rst_ni, - input host_req_t host_req_i, - input logic host_req_valid_i, - output logic host_req_ready_o, - - input host_w_t host_w_i, - input logic host_w_valid_i, - output logic host_w_ready_o, - - output host_r_t host_r_o, - output logic host_r_valid_o, - input logic host_r_ready_i, - - output host_wrsp_t host_wrsp_o, - output logic host_wrsp_valid_o, - input logic host_wrsp_ready_i, + input host_req_t host_link_req_i, + output host_rsp_t host_link_rsp_o, input hyperbus_pkg::frontend_cfg_t frontend_cfg_i, input rule_t [NumChips-1:0] chip_rules_i, output logic trans_active_o, output logic decode_error_o, - input hyper_rx_t rx_i, - input logic rx_valid_i, - output logic rx_ready_o, - - output hyper_tx_t tx_o, - output logic tx_valid_o, - input logic tx_ready_i, - - input logic wrsp_error_i, - input logic wrsp_valid_i, - output logic wrsp_ready_o, - - output hyper_cmd_t cmd_o, - output logic cmd_valid_o, - input logic cmd_ready_i + output hyper_req_t hyper_link_req_o, + input hyper_rsp_t hyper_link_rsp_i ); localparam int unsigned HostDataBytes = HostDataWidth / 8; localparam int unsigned HostBusAddrWidth = $clog2(HostDataBytes); localparam int unsigned PhyDataWidth = NumPhys * 16; - localparam int unsigned PhyDataBytes = PhyDataWidth / 8; localparam int unsigned ChipSelWidth = cf_math_pkg::idx_width(NumChips); `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= 8) @@ -78,6 +55,61 @@ module hyperbus_midend #( typedef logic [HostAddrWidth:0] host_ext_addr_t; typedef logic [ChipSelWidth-1:0] chip_sel_idx_t; + // Unpack the aggregate links at the midend boundary. + host_cmd_t host_cmd_i; + logic host_req_valid_i; + logic host_req_ready_o; + host_w_t host_w_i; + logic host_w_valid_i; + logic host_w_ready_o; + host_r_t host_r_o; + logic host_r_valid_o; + logic host_r_ready_i; + host_wrsp_t host_wrsp_o; + logic host_wrsp_valid_o; + logic host_wrsp_ready_i; + hyper_rx_t rx_i; + logic rx_valid_i; + logic rx_ready_o; + hyper_tx_t tx_o; + logic tx_valid_o; + logic tx_ready_i; + logic wrsp_error_i; + logic wrsp_valid_i; + logic wrsp_ready_o; + hyper_cmd_t cmd_o; + logic cmd_valid_o; + logic cmd_ready_i; + + assign host_cmd_i = host_link_req_i.cmd; + assign host_req_valid_i = host_link_req_i.cmd_valid; + assign host_w_i = host_link_req_i.w; + assign host_w_valid_i = host_link_req_i.w_valid; + assign host_r_ready_i = host_link_req_i.r_ready; + assign host_wrsp_ready_i = host_link_req_i.wrsp_ready; + + assign host_link_rsp_o.cmd_ready = host_req_ready_o; + assign host_link_rsp_o.w_ready = host_w_ready_o; + assign host_link_rsp_o.r = host_r_o; + assign host_link_rsp_o.r_valid = host_r_valid_o; + assign host_link_rsp_o.wrsp = host_wrsp_o; + assign host_link_rsp_o.wrsp_valid = host_wrsp_valid_o; + + assign rx_i = hyper_link_rsp_i.rx; + assign rx_valid_i = hyper_link_rsp_i.rx_valid; + assign tx_ready_i = hyper_link_rsp_i.tx_ready; + assign wrsp_error_i = hyper_link_rsp_i.wrsp.error; + assign wrsp_valid_i = hyper_link_rsp_i.wrsp_valid; + assign cmd_ready_i = hyper_link_rsp_i.cmd_ready; + + assign hyper_link_req_o.rx_ready = rx_ready_o; + assign hyper_link_req_o.tx = tx_o; + assign hyper_link_req_o.tx_valid = tx_valid_o; + assign hyper_link_req_o.wrsp_ready = wrsp_ready_o; + assign hyper_link_req_o.cmd = cmd_o; + assign hyper_link_req_o.cmd_valid = cmd_valid_o; + + // Command decode and invalid-request response tracking. typedef enum logic [1:0] { ErrorIdle, ErrorRead, @@ -85,28 +117,16 @@ module hyperbus_midend #( ErrorWriteResp } error_state_e; - typedef enum logic [2:0] { - AtomicIdle, - AtomicWaitWriteData, - AtomicReadCmd, - AtomicReadData, - AtomicWriteCmd, - AtomicWriteData, - AtomicWriteResp, - AtomicReturn - } atomic_state_e; - - typedef logic [HostDataWidth-1:0] host_data_t; - typedef logic [HostDataBytes-1:0] host_strb_t; - chip_sel_idx_t cmd_chip_sel_idx; - logic cmd_fire; - logic converter_cmd_fire; + chip_sel_idx_t cmd_end_chip_sel_idx; + logic command_accepted; + logic adapter_started; logic cmd_dec_valid; logic cmd_dec_error; + logic cmd_end_dec_valid; logic req_range_valid; logic req_decode_error; - logic error_req_fire; + logic error_req_accepted; error_state_e error_state_d, error_state_q; hyperbus_pkg::hyper_blen_t error_beats_d, error_beats_q; @@ -117,19 +137,19 @@ module hyperbus_midend #( host_w_t converted_host_w; logic converted_host_w_valid; - host_req_t cmd_req; + // Address decode and command segmentation. + host_cmd_t cmd_req; host_addr_t req_phy_first_addr; host_ext_addr_t req_phy_first_ext; host_ext_addr_t req_phy_end_addr; host_ext_addr_t req_last_addr; host_ext_addr_t req_phy_bytes; hyperbus_pkg::hyper_blen_t req_phy_burst; - host_ext_addr_t range_cursor; host_ext_addr_t cmd_rule_end_addr; logic req_addr_overflow; logic atomic_range_valid; - host_req_t segment_req_d, segment_req_q; + host_cmd_t segment_req_d, segment_req_q; hyperbus_pkg::hyper_blen_t segment_remaining_d, segment_remaining_q; hyperbus_pkg::hyper_blen_t cmd_remaining; host_ext_addr_t cmd_rule_capacity; @@ -139,67 +159,43 @@ module hyperbus_midend #( logic segment_final_d, segment_final_q; logic segment_wrsp_error_d, segment_wrsp_error_q; logic cmd_segment_final; - logic normal_cmd_fire; + logic normal_cmd_accepted; logic normal_segment_complete; - host_req_t atomic_req_d, atomic_req_q; - host_w_t atomic_operand_d, atomic_operand_q; - host_w_t atomic_write_d, atomic_write_q; - host_r_t atomic_read_d, atomic_read_q; - hyperbus_pkg::hyper_resp_e atomic_resp_d, atomic_resp_q; - atomic_state_e atomic_state_d, atomic_state_q; - logic atomic_r_pending_d, atomic_r_pending_q; - logic atomic_b_pending_d, atomic_b_pending_q; - logic atomic_req_fire; + // Atomic requests use the same command and data adapters as normal traffic. + logic atomic_req_accepted; logic atomic_cmd_valid; logic atomic_active; + logic atomic_completed; logic normal_req; logic atomic_request_valid; - host_data_t atomic_result; - host_data_t atomic_old_value; - host_data_t atomic_operand_value; - host_data_t atomic_swap_value; - host_data_t atomic_value_mask; - host_data_t atomic_result_value; - int unsigned atomic_byte_offset; - int unsigned atomic_operand_bytes; - int unsigned atomic_operand_bits; - - logic [PhyDataWidth-1:0] rx_data; - logic [PhyDataWidth/2-1:0] rx_data_lower_d; - logic [PhyDataWidth/2-1:0] rx_data_lower_q; - logic rx_error; - logic rx_last; - logic rx_valid; - logic rx_ready; - logic merge_r_d; - logic merge_r_q; - - logic [PhyDataWidth-1:0] tx_data; - logic [PhyDataBytes-1:0] tx_strb; - logic tx_last; - logic tx_valid; - logic tx_ready; - logic split_w_d; - logic split_w_q; + host_cmd_t atomic_cmd; + host_w_t atomic_host_w; + logic atomic_host_w_valid; + logic atomic_host_w_ready; + host_r_t atomic_host_r; + logic atomic_host_r_valid; + host_wrsp_t atomic_host_wrsp; + logic atomic_host_wrsp_valid; + logic atomic_read_ready; + logic atomic_wrsp_ready; + + logic adapter_rx_last; logic trans_active_d; logic trans_active_q; logic trans_active_set; logic trans_active_reset; - logic host_req_fire; + logic host_req_accepted; - assign atomic_active = atomic_state_q != AtomicIdle; - assign normal_req = host_req_i.atomic_op == hyperbus_pkg::HyperAtomicNone; + assign normal_req = host_cmd_i.atomic_op == hyperbus_pkg::HyperAtomicNone; assign atomic_request_valid = - (host_req_i.atomic_op != hyperbus_pkg::HyperAtomicInvalid) && - (host_req_i.beats == 1) && - (host_req_i.burst == hyperbus_pkg::HyperBurstIncr) && - (host_req_i.size <= HostBusAddrWidth) && - ((host_req_i.atomic_op != hyperbus_pkg::HyperAtomicCompare) || - (host_req_i.size != '0)) && + (host_cmd_i.atomic_op != hyperbus_pkg::HyperAtomicInvalid) && + (host_cmd_i.beats == 1) && + (host_cmd_i.burst == hyperbus_pkg::HyperBurstIncr) && + (host_cmd_i.size <= HostBusAddrWidth) && + ((host_cmd_i.atomic_op != hyperbus_pkg::HyperAtomicCompare) || + (host_cmd_i.size != '0)) && atomic_range_valid; - assign atomic_cmd_valid = (atomic_state_q == AtomicReadCmd) || - (atomic_state_q == AtomicWriteCmd); assign cmd_valid_o = atomic_cmd_valid || segment_pending_q || (host_req_valid_i && !trans_active_q && cmd_dec_valid && @@ -207,32 +203,17 @@ module hyperbus_midend #( assign host_req_ready_o = !trans_active_q && (normal_req ? (req_decode_error || (cmd_dec_valid && cmd_ready_i)) : 1'b1); - assign host_req_fire = host_req_valid_i && host_req_ready_o; - assign cmd_fire = cmd_valid_o && cmd_ready_i; - assign normal_cmd_fire = cmd_fire && !atomic_cmd_valid; - assign converter_cmd_fire = atomic_cmd_valid ? cmd_fire : - (normal_cmd_fire && !segment_pending_q); - assign error_req_fire = host_req_valid_i && host_req_ready_o && req_decode_error; - assign atomic_req_fire = host_req_valid_i && host_req_ready_o && !normal_req; - assign decode_error_o = error_req_fire || - (atomic_req_fire && !atomic_range_valid); + assign host_req_accepted = host_req_valid_i && host_req_ready_o; + assign command_accepted = cmd_valid_o && cmd_ready_i; + assign normal_cmd_accepted = command_accepted && !atomic_cmd_valid; + assign adapter_started = atomic_cmd_valid ? command_accepted : + (normal_cmd_accepted && !segment_pending_q); + assign error_req_accepted = host_req_accepted && req_decode_error; + assign atomic_req_accepted = host_req_accepted && !normal_req; + assign decode_error_o = error_req_accepted || + (atomic_req_accepted && !atomic_range_valid); assign trans_active_o = trans_active_q; - addr_decode #( - .NoIndices ( NumChips ), - .NoRules ( NumChips ), - .addr_t ( host_addr_t ), - .rule_t ( rule_t ) - ) i_addr_decode_chip_sel ( - .addr_i ( req_phy_first_addr ), - .addr_map_i ( chip_rules_i ), - .idx_o ( cmd_chip_sel_idx ), - .dec_valid_o ( cmd_dec_valid ), - .dec_error_o ( cmd_dec_error ), - .en_default_idx_i ( 1'b0 ), - .default_idx_i ( '0 ) - ); - always_comb begin : proc_cmd_cs cmd_o.cs = '0; if (cmd_dec_valid) begin @@ -241,20 +222,12 @@ module hyperbus_midend #( end always_comb begin : proc_cmd_req - cmd_req = host_req_i; + cmd_req = host_cmd_i; if (segment_pending_q) begin cmd_req = segment_req_q; end if (atomic_active) begin - cmd_req = atomic_req_q; - cmd_req.write = atomic_state_q != AtomicReadCmd; - end - if (cmd_req.atomic_op != hyperbus_pkg::HyperAtomicNone) begin - cmd_req.beats = 1; - if ((cmd_req.atomic_op == hyperbus_pkg::HyperAtomicCompare) && - (cmd_req.size != '0)) begin - cmd_req.size = cmd_req.size - 1'b1; - end + cmd_req = atomic_cmd; end end @@ -273,67 +246,48 @@ module hyperbus_midend #( (req_phy_end_addr > (host_ext_addr_t'(1) << HostAddrWidth)); end - // Follow decoder priority across the request and find the first segment boundary. + // Software keeps ranges ordered and non-overlapping; transactions may cross contiguous ranges. always_comb begin : proc_req_rule_range - range_cursor = req_phy_first_ext; - cmd_rule_end_addr = req_phy_first_ext; - req_range_valid = !req_addr_overflow; - - for (int unsigned step = 0; step < (2 * NumChips); step++) begin - logic rule_found; - int unsigned selected_rule_idx; - host_ext_addr_t selected_rule_end; - host_ext_addr_t next_transition; - - rule_found = 1'b0; - selected_rule_idx = 0; - selected_rule_end = range_cursor; - for (int unsigned rule_idx = 0; rule_idx < NumChips; rule_idx++) begin - host_ext_addr_t rule_end; - - rule_end = (chip_rules_i[rule_idx].end_addr == '0) ? - (host_ext_addr_t'(1) << HostAddrWidth) : - host_ext_addr_t'(chip_rules_i[rule_idx].end_addr); - if ((range_cursor >= host_ext_addr_t'(chip_rules_i[rule_idx].start_addr)) && - (range_cursor < rule_end)) begin - rule_found = 1'b1; - selected_rule_idx = rule_idx; - selected_rule_end = rule_end; - end - end - next_transition = selected_rule_end; - for (int unsigned rule_idx = 0; rule_idx < NumChips; rule_idx++) begin - host_ext_addr_t rule_start; - - rule_start = host_ext_addr_t'(chip_rules_i[rule_idx].start_addr); - if ((rule_idx > selected_rule_idx) && (rule_start > range_cursor) && - (rule_start < next_transition)) begin - next_transition = rule_start; - end + cmd_chip_sel_idx = '0; + cmd_end_chip_sel_idx = '0; + cmd_dec_valid = 1'b0; + cmd_end_dec_valid = 1'b0; + cmd_rule_end_addr = req_phy_first_ext; + + for (int unsigned i = 0; i < NumChips; i++) begin + host_ext_addr_t rule_start; + host_ext_addr_t rule_end; + + rule_start = host_ext_addr_t'(chip_rules_i[i].start_addr); + rule_end = (chip_rules_i[i].end_addr == '0) ? + (host_ext_addr_t'(1) << HostAddrWidth) : + host_ext_addr_t'(chip_rules_i[i].end_addr); + if (!cmd_dec_valid && (req_phy_first_ext >= rule_start) && + (req_phy_first_ext < rule_end)) begin + cmd_chip_sel_idx = chip_sel_idx_t'(i); + cmd_dec_valid = 1'b1; + cmd_rule_end_addr = rule_end; end - if (step == 0) begin - cmd_rule_end_addr = next_transition; - end - if (range_cursor < req_phy_end_addr) begin - if (rule_found) begin - if ((req_phy_end_addr > next_transition) && - (next_transition[NumPhys-1:0] != '0)) begin - req_range_valid = 1'b0; - end - range_cursor = (req_phy_end_addr < next_transition) ? - req_phy_end_addr : next_transition; - end else begin - req_range_valid = 1'b0; - end + if (!cmd_end_dec_valid && ((req_phy_end_addr - 1'b1) >= rule_start) && + ((req_phy_end_addr - 1'b1) < rule_end)) begin + cmd_end_chip_sel_idx = chip_sel_idx_t'(i); + cmd_end_dec_valid = 1'b1; end end - if (range_cursor < req_phy_end_addr) begin - req_range_valid = 1'b0; + + req_range_valid = !req_addr_overflow && cmd_dec_valid && cmd_end_dec_valid && + (cmd_end_chip_sel_idx >= cmd_chip_sel_idx); + for (int unsigned i = 0; i < NumChips - 1; i++) begin + if ((i >= cmd_chip_sel_idx) && (i < cmd_end_chip_sel_idx) && + (chip_rules_i[i].end_addr != chip_rules_i[i+1].start_addr)) begin + req_range_valid = 1'b0; + end end end - assign atomic_range_valid = cmd_dec_valid && !req_addr_overflow && - (req_phy_end_addr <= cmd_rule_end_addr); + assign cmd_dec_error = !cmd_dec_valid; + assign atomic_range_valid = req_range_valid && + (cmd_chip_sel_idx == cmd_end_chip_sel_idx); assign req_decode_error = normal_req && (cmd_dec_error || @@ -371,305 +325,89 @@ module hyperbus_midend #( cmd_o.trans.burst = cmd_segment_burst; end - always_comb begin : proc_rx_merge - rx_valid = rx_valid_i; - rx_ready_o = rx_ready; - rx_data = rx_i.data; - rx_last = rx_i.last; - rx_error = rx_i.error; - rx_data_lower_d = rx_data_lower_q; - merge_r_d = merge_r_q; - - if ((NumPhys == 2) && !frontend_cfg_i.dual_phy) begin - if (rx_valid_i && rx_ready) begin - merge_r_d = merge_r_q + 1; - end - rx_data = {rx_i.data[PhyDataWidth/2-1:0], rx_data_lower_q}; - rx_valid = rx_valid_i && merge_r_q; - rx_last = rx_i.last && merge_r_q; - rx_error = rx_i.error; - if (!merge_r_q) begin - rx_data_lower_d = rx_i.data[PhyDataWidth/2-1:0]; - end - end - if (!atomic_active) begin - rx_last = rx_last && segment_final_q; - end - end + assign adapter_rx_last = rx_i.last && (atomic_active || segment_final_q); - hyperbus_phy2r #( + hyperbus_read_adapter #( .HostDataWidth ( HostDataWidth ), .BurstLength ( hyperbus_pkg::HyperBurstWidth ), .T ( host_r_t ), .NumPhys ( NumPhys ) - ) i_hyperbus_phy2r ( + ) i_read_adapter ( .clk_i, .rst_ni, .size_i ( cmd_req.size ), - .cmd_fire_i ( converter_cmd_fire ), + .start_i ( adapter_started && !cmd_req.write ), + .dual_phy_i ( frontend_cfg_i.dual_phy ), .start_addr_i ( cmd_req.addr[HostBusAddrWidth-1:0] ), .burst_len_i ( cmd_req.beats ), - .is_read_i ( !cmd_req.write ), - .phy_valid_i ( rx_valid ), - .phy_ready_o ( rx_ready ), - .data_i ( rx_data ), - .last_i ( rx_last ), - .error_i ( rx_error ), + .phy_valid_i ( rx_valid_i ), + .phy_ready_o ( rx_ready_o ), + .data_i ( rx_i.data ), + .last_i ( adapter_rx_last ), + .error_i ( rx_i.error ), .host_valid_o ( converted_host_r_valid ), .host_ready_i ( converted_host_r_ready ), .data_o ( converted_host_r ) ); - `FFARN(merge_r_q, merge_r_d, 1'b0, clk_i, rst_ni) - `FFARN(rx_data_lower_q, rx_data_lower_d, '0, clk_i, rst_ni) - - hyperbus_w2phy #( + hyperbus_write_adapter #( .HostDataWidth ( HostDataWidth ), .T ( host_w_t ), .NumPhys ( NumPhys ) - ) i_hyperbus_w2phy ( + ) i_write_adapter ( .clk_i, .rst_ni, .size_i ( cmd_req.size ), - .is_write_i ( cmd_req.write ), - .cmd_fire_i ( converter_cmd_fire ), + .start_i ( adapter_started && cmd_req.write ), + .dual_phy_i ( frontend_cfg_i.dual_phy ), .start_addr_i ( cmd_req.addr[HostBusAddrWidth-1:0] ), .data_i ( converted_host_w ), .host_valid_i ( converted_host_w_valid ), .host_ready_o ( converted_host_w_ready ), - .data_o ( tx_data ), - .last_o ( tx_last ), - .strb_o ( tx_strb ), - .phy_valid_o ( tx_valid ), - .phy_ready_i ( tx_ready ) + .data_o ( tx_o.data ), + .last_o ( tx_o.last ), + .strb_o ( tx_o.strb ), + .phy_valid_o ( tx_valid_o ), + .phy_ready_i ( tx_ready_i ) ); - always_comb begin : proc_tx_split - tx_o.data = tx_data; - tx_o.strb = tx_strb; - tx_o.last = tx_last; - tx_valid_o = tx_valid; - tx_ready = tx_ready_i; - split_w_d = split_w_q; - - if ((NumPhys == 2) && !frontend_cfg_i.dual_phy) begin - if (tx_valid && tx_ready_i) begin - split_w_d = split_w_q + 1; - end - tx_o.last = tx_last && split_w_q; - tx_valid_o = tx_valid; - tx_ready = tx_ready_i && split_w_q; - tx_o.data = {tx_data[PhyDataWidth/2-1:0], tx_data[PhyDataWidth/2-1:0]}; - tx_o.strb = {tx_strb[NumPhys-1:0], tx_strb[NumPhys-1:0]}; - if (split_w_q) begin - tx_o.data = {tx_data[PhyDataWidth-1:PhyDataWidth/2], - tx_data[PhyDataWidth-1:PhyDataWidth/2]}; - tx_o.strb = {tx_strb[NumPhys*2-1:NumPhys], tx_strb[NumPhys*2-1:NumPhys]}; - end - end - end - - `FFARN(split_w_q, split_w_d, 1'b0, clk_i, rst_ni) - - always_comb begin : proc_atomic_alu - atomic_byte_offset = 0; - atomic_operand_bytes = 1; - atomic_operand_bits = 8; - atomic_value_mask = '1; - atomic_old_value = atomic_read_q.data; - atomic_operand_value = atomic_operand_q.data; - atomic_swap_value = atomic_operand_q.data >> 8; - atomic_result_value = atomic_old_value; - atomic_result = atomic_read_q.data; - - // Malformed atomics are drained and returned without evaluating invalid widths. - if (atomic_resp_q == hyperbus_pkg::HyperRespOkay) begin - atomic_byte_offset = unsigned'(atomic_req_q.addr[HostBusAddrWidth-1:0]); - atomic_operand_bytes = 1 << ((atomic_req_q.atomic_op == - hyperbus_pkg::HyperAtomicCompare) ? - (atomic_req_q.size - 1'b1) : atomic_req_q.size); - atomic_operand_bits = atomic_operand_bytes * 8; - - if (atomic_operand_bits < HostDataWidth) begin - atomic_value_mask = atomic_value_mask >> (HostDataWidth - atomic_operand_bits); - end - - atomic_old_value = atomic_read_q.data >> (atomic_byte_offset * 8); - atomic_operand_value = atomic_operand_q.data >> (atomic_byte_offset * 8); - atomic_swap_value = atomic_operand_value >> atomic_operand_bits; - atomic_result_value = atomic_old_value; - - unique case (atomic_req_q.atomic_op) - hyperbus_pkg::HyperAtomicSwap: begin - atomic_result_value = atomic_operand_value; - end - hyperbus_pkg::HyperAtomicCompare: begin - if ((atomic_old_value & atomic_value_mask) == - (atomic_operand_value & atomic_value_mask)) begin - atomic_result_value = atomic_swap_value; - end - end - hyperbus_pkg::HyperAtomicAdd: begin - atomic_result_value = atomic_old_value + atomic_operand_value; - end - hyperbus_pkg::HyperAtomicClear: begin - atomic_result_value = atomic_old_value & ~atomic_operand_value; - end - hyperbus_pkg::HyperAtomicXor: begin - atomic_result_value = atomic_old_value ^ atomic_operand_value; - end - hyperbus_pkg::HyperAtomicSet: begin - atomic_result_value = atomic_old_value | atomic_operand_value; - end - hyperbus_pkg::HyperAtomicSignedMax: begin - if (atomic_old_value[atomic_operand_bits-1] != - atomic_operand_value[atomic_operand_bits-1]) begin - if (atomic_old_value[atomic_operand_bits-1]) begin - atomic_result_value = atomic_operand_value; - end - end else if ((atomic_old_value & atomic_value_mask) < - (atomic_operand_value & atomic_value_mask)) begin - atomic_result_value = atomic_operand_value; - end - end - hyperbus_pkg::HyperAtomicSignedMin: begin - if (atomic_old_value[atomic_operand_bits-1] != - atomic_operand_value[atomic_operand_bits-1]) begin - if (atomic_operand_value[atomic_operand_bits-1]) begin - atomic_result_value = atomic_operand_value; - end - end else if ((atomic_old_value & atomic_value_mask) > - (atomic_operand_value & atomic_value_mask)) begin - atomic_result_value = atomic_operand_value; - end - end - hyperbus_pkg::HyperAtomicUnsignedMax: begin - if ((atomic_old_value & atomic_value_mask) < - (atomic_operand_value & atomic_value_mask)) begin - atomic_result_value = atomic_operand_value; - end - end - hyperbus_pkg::HyperAtomicUnsignedMin: begin - if ((atomic_old_value & atomic_value_mask) > - (atomic_operand_value & atomic_value_mask)) begin - atomic_result_value = atomic_operand_value; - end - end - default:; - endcase - - atomic_result = atomic_read_q.data & - ~(atomic_value_mask << (atomic_byte_offset * 8)); - atomic_result |= (atomic_result_value & atomic_value_mask) << - (atomic_byte_offset * 8); - end - end - - always_comb begin : proc_atomic_control - atomic_state_d = atomic_state_q; - atomic_req_d = atomic_req_q; - atomic_operand_d = atomic_operand_q; - atomic_write_d = atomic_write_q; - atomic_read_d = atomic_read_q; - atomic_resp_d = atomic_resp_q; - atomic_r_pending_d = atomic_r_pending_q; - atomic_b_pending_d = atomic_b_pending_q; - - unique case (atomic_state_q) - AtomicIdle: begin - if (atomic_req_fire) begin - atomic_req_d = host_req_i; - atomic_resp_d = hyperbus_pkg::HyperRespOkay; - if (!atomic_request_valid) begin - atomic_resp_d = hyperbus_pkg::HyperRespAtomicError; - end - atomic_state_d = AtomicWaitWriteData; - end - end - AtomicWaitWriteData: begin - if (host_w_valid_i && host_w_ready_o) begin - atomic_operand_d = host_w_i; - if (atomic_resp_q == hyperbus_pkg::HyperRespOkay) begin - atomic_state_d = AtomicReadCmd; - end else if (host_w_i.last) begin - atomic_r_pending_d = atomic_req_q.atomic_return; - atomic_b_pending_d = 1'b1; - atomic_state_d = AtomicReturn; - end - end - end - AtomicReadCmd: begin - if (cmd_fire) begin - atomic_state_d = AtomicReadData; - end - end - AtomicReadData: begin - if (converted_host_r_valid && converted_host_r_ready) begin - atomic_read_d = converted_host_r; - if (converted_host_r.resp != hyperbus_pkg::HyperRespOkay) begin - atomic_resp_d = converted_host_r.resp; - atomic_r_pending_d = atomic_req_q.atomic_return; - atomic_b_pending_d = 1'b1; - atomic_state_d = AtomicReturn; - end else begin - atomic_state_d = AtomicWriteCmd; - end - end - end - AtomicWriteCmd: begin - atomic_write_d.data = atomic_result; - atomic_write_d.strb = '0; - atomic_write_d.last = 1'b1; - for (int unsigned i = 0; i < HostDataBytes; i++) begin - if ((i >= atomic_byte_offset) && - (i < atomic_byte_offset + atomic_operand_bytes)) begin - atomic_write_d.strb[i] = 1'b1; - end - end - if (cmd_fire) begin - atomic_state_d = AtomicWriteData; - end - end - AtomicWriteData: begin - if (converted_host_w_valid && converted_host_w_ready) begin - atomic_state_d = AtomicWriteResp; - end - end - AtomicWriteResp: begin - if (wrsp_valid_i && wrsp_ready_o) begin - atomic_resp_d = wrsp_error_i ? hyperbus_pkg::HyperRespAccessError : - hyperbus_pkg::HyperRespOkay; - atomic_r_pending_d = atomic_req_q.atomic_return; - atomic_b_pending_d = 1'b1; - atomic_state_d = AtomicReturn; - end - end - AtomicReturn: begin - if (host_r_valid_o && host_r_ready_i) begin - atomic_r_pending_d = 1'b0; - end - if (host_wrsp_valid_o && host_wrsp_ready_i) begin - atomic_b_pending_d = 1'b0; - end - if ((!atomic_r_pending_q || (host_r_valid_o && host_r_ready_i)) && - (!atomic_b_pending_q || (host_wrsp_valid_o && host_wrsp_ready_i))) begin - atomic_state_d = AtomicIdle; - end - end - default: begin - atomic_state_d = AtomicIdle; - end - endcase - end - - `FFARN(atomic_state_q, atomic_state_d, AtomicIdle, clk_i, rst_ni) - `FFARN(atomic_req_q, atomic_req_d, '0, clk_i, rst_ni) - `FFARN(atomic_operand_q, atomic_operand_d, '0, clk_i, rst_ni) - `FFARN(atomic_write_q, atomic_write_d, '0, clk_i, rst_ni) - `FFARN(atomic_read_q, atomic_read_d, '0, clk_i, rst_ni) - `FFARN(atomic_resp_q, atomic_resp_d, hyperbus_pkg::HyperRespOkay, clk_i, rst_ni) - `FFARN(atomic_r_pending_q, atomic_r_pending_d, 1'b0, clk_i, rst_ni) - `FFARN(atomic_b_pending_q, atomic_b_pending_d, 1'b0, clk_i, rst_ni) + hyperbus_atomic_handler #( + .HostAddrWidth ( HostAddrWidth ), + .HostDataWidth ( HostDataWidth ), + .host_cmd_t ( host_cmd_t ), + .host_w_t ( host_w_t ), + .host_r_t ( host_r_t ), + .host_wrsp_t ( host_wrsp_t ) + ) i_atomic_handler ( + .clk_i, + .rst_ni, + .start_i ( atomic_req_accepted ), + .request_valid_i ( atomic_request_valid ), + .request_i ( host_cmd_i ), + .active_o ( atomic_active ), + .completed_o ( atomic_completed ), + .command_o ( atomic_cmd ), + .command_valid_o ( atomic_cmd_valid ), + .command_ready_i ( cmd_ready_i ), + .host_w_i ( host_w_i ), + .host_w_valid_i ( host_w_valid_i ), + .host_w_ready_o ( atomic_host_w_ready ), + .host_r_o ( atomic_host_r ), + .host_r_valid_o ( atomic_host_r_valid ), + .host_r_ready_i ( host_r_ready_i ), + .host_wrsp_o ( atomic_host_wrsp ), + .host_wrsp_valid_o ( atomic_host_wrsp_valid ), + .host_wrsp_ready_i ( host_wrsp_ready_i ), + .read_i ( converted_host_r ), + .read_valid_i ( converted_host_r_valid ), + .read_ready_o ( atomic_read_ready ), + .write_o ( atomic_host_w ), + .write_valid_o ( atomic_host_w_valid ), + .write_ready_i ( converted_host_w_ready ), + .write_rsp_error_i ( wrsp_error_i ), + .write_rsp_valid_i ( wrsp_valid_i ), + .write_rsp_ready_o ( atomic_wrsp_ready ) + ); assign normal_segment_complete = trans_active_q && !atomic_active && (segment_req_q.write ? (wrsp_valid_i && wrsp_ready_o) : @@ -688,7 +426,7 @@ module hyperbus_midend #( segment_wrsp_error_d = 1'b1; end end - if (normal_cmd_fire) begin + if (normal_cmd_accepted) begin segment_pending_d = 1'b0; segment_final_d = cmd_segment_final; segment_req_d = cmd_req; @@ -712,9 +450,9 @@ module hyperbus_midend #( unique case (error_state_q) ErrorIdle: begin - if (error_req_fire) begin - error_beats_d = host_req_i.beats; - error_state_d = host_req_i.write ? ErrorWrite : ErrorRead; + if (error_req_accepted) begin + error_beats_d = host_cmd_i.beats; + error_state_d = host_cmd_i.write ? ErrorWrite : ErrorRead; end end ErrorRead: begin @@ -756,42 +494,15 @@ module hyperbus_midend #( wrsp_ready_o = segment_final_q ? host_wrsp_ready_i : 1'b1; if (atomic_active) begin - converted_host_w = '0; - converted_host_w_valid = 1'b0; - host_w_ready_o = 1'b0; - host_r_o = '0; - host_r_valid_o = 1'b0; - converted_host_r_ready = 1'b0; - host_wrsp_o = '0; - host_wrsp_valid_o = 1'b0; - wrsp_ready_o = 1'b0; - - unique case (atomic_state_q) - AtomicWaitWriteData: begin - host_w_ready_o = 1'b1; - end - AtomicReadData: begin - converted_host_r_ready = 1'b1; - end - AtomicWriteData: begin - converted_host_w = atomic_write_q; - converted_host_w_valid = 1'b1; - end - AtomicWriteResp: begin - wrsp_ready_o = 1'b1; - end - AtomicReturn: begin - host_r_o = atomic_read_q; - host_r_o.resp = atomic_resp_q; - host_r_o.last = 1'b1; - host_r_o.atomic_ok = atomic_resp_q == hyperbus_pkg::HyperRespOkay; - host_r_valid_o = atomic_r_pending_q; - host_wrsp_o.resp = atomic_resp_q; - host_wrsp_o.atomic_ok = atomic_resp_q == hyperbus_pkg::HyperRespOkay; - host_wrsp_valid_o = atomic_b_pending_q; - end - default:; - endcase + converted_host_w = atomic_host_w; + converted_host_w_valid = atomic_host_w_valid; + host_w_ready_o = atomic_host_w_ready; + host_r_o = atomic_host_r; + host_r_valid_o = atomic_host_r_valid; + converted_host_r_ready = atomic_read_ready; + host_wrsp_o = atomic_host_wrsp; + host_wrsp_valid_o = atomic_host_wrsp_valid; + wrsp_ready_o = atomic_wrsp_ready; end if (error_state_q != ErrorIdle) begin @@ -813,12 +524,9 @@ module hyperbus_midend #( `FFARN(error_state_q, error_state_d, ErrorIdle, clk_i, rst_ni) `FFARN(error_beats_q, error_beats_d, '0, clk_i, rst_ni) - assign trans_active_set = (normal_cmd_fire && !segment_pending_q) || - error_req_fire || atomic_req_fire; - assign trans_active_reset = atomic_active ? - ((atomic_state_q == AtomicReturn) && - (!atomic_r_pending_q || (host_r_valid_o && host_r_ready_i)) && - (!atomic_b_pending_q || (host_wrsp_valid_o && host_wrsp_ready_i))) : + assign trans_active_set = (normal_cmd_accepted && !segment_pending_q) || + error_req_accepted || atomic_req_accepted; + assign trans_active_reset = atomic_active ? atomic_completed : ((host_r_valid_o && host_r_ready_i && host_r_o.last) || (host_wrsp_valid_o && host_wrsp_ready_i)); @@ -835,16 +543,16 @@ module hyperbus_midend #( `FFARN(trans_active_q, trans_active_d, 1'b0, clk_i, rst_ni) - `ASSERT_KNOWN_IF(HostReqKnown, host_req_i, host_req_fire) - `ASSERT(HostReqBeatsNonzero, host_req_fire |-> (host_req_i.beats != '0)) - `ASSERT(HostReqSizeValid, host_req_fire |-> (host_req_i.size <= HostBusAddrWidth)) - `ASSERT(HostReqBurstValid, host_req_fire |-> - ((host_req_i.burst == hyperbus_pkg::HyperBurstIncr) || - ((host_req_i.burst == hyperbus_pkg::HyperBurstFixed) && (host_req_i.beats == 1)))) - `ASSERT(HostReqAtomicValid, host_req_fire |-> - (host_req_i.atomic_op <= hyperbus_pkg::HyperAtomicUnsignedMin)) - `ASSERT(HostReqAtomicWrite, (host_req_fire && - (host_req_i.atomic_op != hyperbus_pkg::HyperAtomicNone)) |-> host_req_i.write) - `ASSERT(BackendBurstNonzero, cmd_fire |-> (cmd_o.trans.burst != '0)) + `ASSERT_KNOWN_IF(HostReqKnown, host_cmd_i, host_req_accepted) + `ASSERT(HostReqBeatsNonzero, host_req_accepted |-> (host_cmd_i.beats != '0)) + `ASSERT(HostReqSizeValid, host_req_accepted |-> (host_cmd_i.size <= HostBusAddrWidth)) + `ASSERT(HostReqBurstValid, host_req_accepted |-> + ((host_cmd_i.burst == hyperbus_pkg::HyperBurstIncr) || + ((host_cmd_i.burst == hyperbus_pkg::HyperBurstFixed) && (host_cmd_i.beats == 1)))) + `ASSERT(HostReqAtomicValid, host_req_accepted |-> + (host_cmd_i.atomic_op <= hyperbus_pkg::HyperAtomicUnsignedMin)) + `ASSERT(HostReqAtomicWrite, (host_req_accepted && + (host_cmd_i.atomic_op != hyperbus_pkg::HyperAtomicNone)) |-> host_cmd_i.write) + `ASSERT(BackendBurstNonzero, command_accepted |-> (cmd_o.trans.burst != '0)) endmodule diff --git a/src/hyperbus_phy_if.sv b/src/hyperbus_phy_if.sv deleted file mode 100644 index 37eac1e..0000000 --- a/src/hyperbus_phy_if.sv +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2023 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 -// -// Luca Valente -// Philippe Sauter - -`include "common_cells/registers.svh" - -module hyperbus_phy_if import hyperbus_pkg::*; #( - parameter int unsigned NumChips = 2, - parameter int unsigned NumPhys = 2, - parameter int unsigned StartupCycles = 60000, - parameter int unsigned SyncStages = 2, - parameter type hyper_tx_t = logic, - parameter type hyper_rx_t = logic -) ( - input logic clk_phy_i, - input logic clk_phy_i_90, - input logic rst_phy_ni, - input logic test_mode_i, - input phy_cfg_t cfg_i, - output logic busy_o, - - input logic trans_valid_i, - output logic trans_ready_o, - input hyper_tf_t trans_i, - input logic [NumChips-1:0] trans_cs_i, - - input logic tx_valid_i, - output logic tx_ready_o, - input hyper_tx_t tx_i, - - output logic rx_valid_o, - input logic rx_ready_i, - output hyper_rx_t rx_o, - - output logic b_valid_o, - input logic b_ready_i, - output logic b_error_o, - - // Physical interface: facing HyperBus - output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, - output logic [NumPhys-1:0] hyper_ck_o, - output logic [NumPhys-1:0] hyper_ck_no, - output logic [NumPhys-1:0] hyper_rwds_o, - input logic [NumPhys-1:0] hyper_rwds_i, - output logic [NumPhys-1:0] hyper_rwds_oe_o, - input logic [NumPhys-1:0][7:0] hyper_dq_i, - output logic [NumPhys-1:0][7:0] hyper_dq_o, - output logic [NumPhys-1:0] hyper_dq_oe_o, - output logic [NumPhys-1:0] hyper_reset_no -); - - logic clk_phy_0, clk_phy_90; - - assign clk_phy_0 = clk_phy_i; - assign clk_phy_90 = clk_phy_i_90; - - - phy_rx_t [NumPhys-1:0] phy_fifo_rx; - phy_rx_t [NumPhys-1:0] fifo_axi_rx; - logic [NumPhys-1:0] phy_fifo_valid; - logic [NumPhys-1:0] phy_fifo_ready; - logic [NumPhys-1:0] fifo_axi_valid; - logic [NumPhys-1:0][1:0] fifo_axi_usage; - logic [NumPhys-1:0] phy_tx_ready; - logic [NumPhys-1:0] phy_tx_valid; - logic [NumPhys-1:0] phy_trans_ready; - logic [NumPhys-1:0] phy_trans_valid; - logic [NumPhys-1:0] phy_b_valid; - logic [NumPhys-1:0] phy_b_error; - logic [NumPhys-1:0] phy_b_ready; - - if (NumPhys == 2) begin : gen_dual_phy - - logic [NumPhys-1:0] phy_enable; - logic [NumPhys-1:0] phy_busy; - logic [NumPhys-1:0] phy_active_q, phy_active_d; - logic [NumPhys-1:0] tx_fork_valid; - logic [NumPhys-1:0] trans_fork_valid; - logic [NumPhys-1:0] rx_join_ready; - logic change_phy_active; - - assign change_phy_active = phy_active_q != phy_enable; - assign phy_enable = cfg_i.dual_phy ? '1 : 2'b01; - assign phy_active_d = change_phy_active && (fifo_axi_usage == '0) ? - (phy_enable | phy_busy) : phy_active_q; - - `FFARN(phy_active_q, phy_active_d, '1, clk_phy_0, rst_phy_ni) - - assign busy_o = |phy_busy; - assign rx_o.error = |({fifo_axi_rx[1].error, fifo_axi_rx[0].error} & phy_active_q); - assign rx_o.last = &({fifo_axi_rx[1].last, fifo_axi_rx[0].last} | ~phy_active_q); - assign b_error_o = |(phy_b_error & phy_active_q); - - stream_fork #( - .N_OUP ( NumPhys ) - ) i_tx_fork ( - .clk_i ( clk_phy_0 ), - .rst_ni ( rst_phy_ni ), - .valid_i ( tx_valid_i ), - .ready_o ( tx_ready_o ), - .valid_o ( tx_fork_valid ), - .ready_i ( phy_tx_ready | ~phy_active_q ) - ); - - assign phy_tx_valid = tx_fork_valid & phy_active_q; - - stream_fork #( - .N_OUP ( NumPhys ) - ) i_trans_fork ( - .clk_i ( clk_phy_0 ), - .rst_ni ( rst_phy_ni ), - .valid_i ( trans_valid_i && !change_phy_active ), - .ready_o ( trans_ready_o ), - .valid_o ( trans_fork_valid ), - .ready_i ( phy_trans_ready | ~phy_active_q ) - ); - - assign phy_trans_valid = trans_fork_valid & phy_active_q; - - stream_join #( - .N_INP ( NumPhys ) - ) i_rx_join ( - .inp_valid_i ( fifo_axi_valid | ~phy_active_q ), - .inp_ready_o ( rx_join_ready ), - .oup_valid_o ( rx_valid_o ), - .oup_ready_i ( rx_ready_i ) - ); - - stream_join #( - .N_INP ( NumPhys ) - ) i_wrsp_join ( - .inp_valid_i ( phy_b_valid | ~phy_active_q ), - .inp_ready_o ( phy_b_ready ), - .oup_valid_o ( b_valid_o ), - .oup_ready_i ( b_ready_i ) - ); - - for (genvar i = 0; i < NumPhys; i++) begin : gen_phy - assign rx_o.data[i*16 +: 16] = fifo_axi_rx[i].data; - - stream_fifo #( - .FALL_THROUGH ( 1'b0 ), - .DEPTH ( 4 ), - .T ( phy_rx_t ) - ) i_rx_fifo ( - .clk_i ( clk_phy_0 ), - .rst_ni ( rst_phy_ni ), - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o ( fifo_axi_usage[i] ), - .data_i ( phy_fifo_rx[i] ), - .valid_i ( phy_fifo_valid[i] ), - .ready_o ( phy_fifo_ready[i] ), - .data_o ( fifo_axi_rx[i] ), - .valid_o ( fifo_axi_valid[i] ), - .ready_i ( rx_join_ready[i] ) - ); - - hyperbus_phy #( - .NumChips ( NumChips ), - .StartupCycles ( StartupCycles ), - .NumPhys ( NumPhys ), - .SyncStages ( SyncStages ) - ) i_phy ( - .clk_i ( clk_phy_0 ), - .clk_i_90 ( clk_phy_90 ), - .rst_ni ( rst_phy_ni ), - .test_mode_i ( test_mode_i ), - - .cfg_i ( cfg_i ), - - .busy_o ( phy_busy[i] ), - - .rx_data_o ( phy_fifo_rx[i].data ), - .rx_last_o ( phy_fifo_rx[i].last ), - .rx_error_o ( phy_fifo_rx[i].error), - .rx_valid_o ( phy_fifo_valid[i] ), - .rx_ready_i ( phy_fifo_ready[i] ), - - .tx_data_i ( tx_i.data[16*i +: 16] ), - .tx_strb_i ( tx_i.strb[2*i +: 2] ), - .tx_last_i ( tx_i.last ), - .tx_valid_i ( phy_tx_valid[i] ), - .tx_ready_o ( phy_tx_ready[i] ), - - .b_error_o ( phy_b_error[i] ), - .b_valid_o ( phy_b_valid[i] ), - .b_ready_i ( phy_b_ready[i] ), - - .trans_i ( trans_i ), - .trans_cs_i ( trans_cs_i ), - .trans_valid_i ( phy_trans_valid[i] ), - .trans_ready_o ( phy_trans_ready[i] ), - - .hyper_cs_no ( hyper_cs_no[i] ), - .hyper_ck_o ( hyper_ck_o[i] ), - .hyper_ck_no ( hyper_ck_no[i] ), - .hyper_rwds_o ( hyper_rwds_o[i] ), - .hyper_rwds_i ( hyper_rwds_i[i] ), - .hyper_rwds_oe_o( hyper_rwds_oe_o[i] ), - .hyper_dq_i ( hyper_dq_i[i] ), - .hyper_dq_o ( hyper_dq_o[i] ), - .hyper_dq_oe_o ( hyper_dq_oe_o[i] ), - .hyper_reset_no ( hyper_reset_no[i] ) - ); - end - end else begin : gen_single_phy - - hyperbus_phy #( - .NumChips ( NumChips ), - .StartupCycles ( StartupCycles ), - .NumPhys ( NumPhys ), - .SyncStages ( SyncStages ) - ) i_phy ( - .clk_i ( clk_phy_0 ), - .clk_i_90 ( clk_phy_90 ), - .rst_ni ( rst_phy_ni ), - .test_mode_i ( test_mode_i ), - - .cfg_i ( cfg_i ), - - .busy_o ( busy_o ), - - .rx_data_o ( rx_o.data ), - .rx_last_o ( rx_o.last ), - .rx_error_o ( rx_o.error ), - .rx_valid_o ( rx_valid_o ), - .rx_ready_i ( rx_ready_i ), - - .tx_data_i ( tx_i.data ), - .tx_strb_i ( tx_i.strb ), - .tx_last_i ( tx_i.last ), - .tx_valid_i ( tx_valid_i ), - .tx_ready_o ( tx_ready_o ), - - .b_error_o ( b_error_o ), - .b_valid_o ( b_valid_o ), - .b_ready_i ( b_ready_i ), - - .trans_i ( trans_i ), - .trans_cs_i ( trans_cs_i ), - .trans_valid_i ( trans_valid_i ), - .trans_ready_o ( trans_ready_o ), - - .hyper_cs_no ( hyper_cs_no ), - .hyper_ck_o ( hyper_ck_o ), - .hyper_ck_no ( hyper_ck_no ), - .hyper_rwds_o ( hyper_rwds_o ), - .hyper_rwds_i ( hyper_rwds_i ), - .hyper_rwds_oe_o( hyper_rwds_oe_o ), - .hyper_dq_i ( hyper_dq_i ), - .hyper_dq_o ( hyper_dq_o ), - .hyper_dq_oe_o ( hyper_dq_oe_o ), - .hyper_reset_no ( hyper_reset_no ) - ); - end - -endmodule diff --git a/src/hyperbus_phy2r.sv b/src/hyperbus_read_adapter.sv similarity index 65% rename from src/hyperbus_phy2r.sv rename to src/hyperbus_read_adapter.sv index 49a7921..658c6e8 100644 --- a/src/hyperbus_phy2r.sv +++ b/src/hyperbus_read_adapter.sv @@ -6,7 +6,7 @@ `include "common_cells/registers.svh" -module hyperbus_phy2r #( +module hyperbus_read_adapter #( parameter int unsigned HostDataWidth = -1, parameter int unsigned NumPhys = -1, parameter type T = logic, @@ -16,8 +16,8 @@ module hyperbus_phy2r #( input logic clk_i, input logic rst_ni, input logic [2:0] size_i, - input logic is_read_i, - input logic cmd_fire_i, + input logic start_i, + input logic dual_phy_i, input logic [AddrWidth-1:0] start_addr_i, input logic [BurstLength-1:0] burst_len_i, output logic host_valid_o, @@ -41,9 +41,9 @@ module hyperbus_phy2r #( WaitData, Sample, CntReady - } hyper_splitter_state_e; + } read_adapter_state_e; - hyper_splitter_state_e state_d, state_q; + read_adapter_state_e state_d, state_q; logic [BurstLength-1:0] byte_host_addr_d, byte_host_addr_q; logic [BurstLength-1:0] byte_phy_cnt_d, byte_phy_cnt_q; logic [BurstLength-1:0] last_addr_d, last_addr_q; @@ -55,6 +55,11 @@ module hyperbus_phy2r #( logic sent_available_data; logic [BurstLength-1:0] next_host_addr; logic host_last; + logic [16*NumPhys-1:0] converted_data; + logic converted_last; + logic converted_error; + logic converted_valid; + logic converted_ready; assign word_cnt = (PhyBeatsPerHost == 1) ? '0 : byte_phy_cnt_q[($clog2(NumPhys) + 1) +: WordCntWidth]; @@ -75,7 +80,7 @@ module hyperbus_phy2r #( last_addr_d = last_addr_q; size_d = size_q; - if (cmd_fire_i && is_read_i) begin + if (start_i) begin byte_host_addr_d[BurstLength-1:AddrWidth] = '0; byte_host_addr_d[AddrWidth-1:0] = start_addr_i; byte_phy_cnt_d[BurstLength-1:AddrWidth] = '0; @@ -86,7 +91,7 @@ module hyperbus_phy2r #( if (host_valid_o && host_ready_i) begin byte_host_addr_d = ((byte_host_addr_q >> size_q) << size_q) + (1 << size_q); end - if (phy_valid_i && phy_ready_o) begin + if (converted_valid && converted_ready) begin byte_phy_cnt_d = byte_phy_cnt_q + NumPhys * 2; end end @@ -102,12 +107,12 @@ module hyperbus_phy2r #( if (host_valid_o && host_ready_i) begin data_buffer_d.resp = hyperbus_pkg::HyperRespOkay; end - if (phy_ready_o && phy_valid_i) begin - data_buffer_d.data[word_cnt*(16*NumPhys) +: (16*NumPhys)] = data_i; - if (error_i) begin + if (converted_ready && converted_valid) begin + data_buffer_d.data[word_cnt*(16*NumPhys) +: (16*NumPhys)] = converted_data; + if (converted_error) begin data_buffer_d.resp = hyperbus_pkg::HyperRespAccessError; end - data_buffer_d.last = last_i; + data_buffer_d.last = converted_last; end end end @@ -115,35 +120,35 @@ module hyperbus_phy2r #( always_comb begin : proc_fsm state_d = state_q; host_valid_o = 1'b0; - phy_ready_o = 1'b0; + converted_ready = 1'b0; unique case (state_q) Idle: begin - if (cmd_fire_i && is_read_i) begin + if (start_i) begin state_d = WaitData; end end WaitData: begin - phy_ready_o = 1'b1; - if (phy_valid_i) begin + converted_ready = 1'b1; + if (converted_valid) begin state_d = Sample; end end Sample: begin - phy_ready_o = 1'b1; + converted_ready = 1'b1; if (enough_data) begin state_d = CntReady; end end CntReady: begin host_valid_o = enough_data_q; - phy_ready_o = !enough_data_q; + converted_ready = !enough_data_q; if (host_valid_o && host_ready_i) begin if (data_o.last || (last_addr_q == byte_host_addr_d)) begin state_d = Idle; end else if (sent_available_data) begin - phy_ready_o = 1'b1; - state_d = phy_valid_i ? Sample : WaitData; + converted_ready = 1'b1; + state_d = converted_valid ? Sample : WaitData; end end end @@ -153,11 +158,59 @@ module hyperbus_phy2r #( endcase end + if (NumPhys == 2) begin : gen_dual_phy + logic [15:0] lower_data_d, lower_data_q; + logic lower_error_d, lower_error_q; + logic merge_d, merge_q; + + always_comb begin : proc_phy_width + converted_data = data_i; + converted_last = last_i; + converted_error = error_i; + converted_valid = phy_valid_i; + phy_ready_o = converted_ready; + lower_data_d = lower_data_q; + lower_error_d = lower_error_q; + merge_d = merge_q; + + if (!dual_phy_i) begin + converted_data = {data_i[15:0], lower_data_q}; + converted_last = last_i && merge_q; + converted_error = error_i || lower_error_q; + converted_valid = phy_valid_i && merge_q; + if (phy_valid_i && phy_ready_o) begin + merge_d = !merge_q; + if (!merge_q) begin + lower_data_d = data_i[15:0]; + lower_error_d = error_i; + end else begin + lower_error_d = 1'b0; + end + end + end + if (start_i) begin + merge_d = 1'b0; + lower_error_d = 1'b0; + end + end + + `FFARN(lower_data_q, lower_data_d, '0, clk_i, rst_ni) + `FFARN(lower_error_q, lower_error_d, 1'b0, clk_i, rst_ni) + `FFARN(merge_q, merge_d, 1'b0, clk_i, rst_ni) + end else begin : gen_single_phy + always_comb begin : proc_phy_width + converted_data = data_i; + converted_last = last_i; + converted_error = error_i; + converted_valid = phy_valid_i; + phy_ready_o = converted_ready; + end + end + `FFARN(state_q, state_d, Idle, clk_i, rst_ni) `FFARN(data_buffer_q, data_buffer_d, '0, clk_i, rst_ni) `FFARN(byte_host_addr_q, byte_host_addr_d, '0, clk_i, rst_ni) `FFARN(byte_phy_cnt_q, byte_phy_cnt_d, '0, clk_i, rst_ni) `FFARN(size_q, size_d, '0, clk_i, rst_ni) `FFARN(last_addr_q, last_addr_d, '0, clk_i, rst_ni) - -endmodule +endmodule : hyperbus_read_adapter diff --git a/src/hyperbus_synchronous.sv b/src/hyperbus_synchronous.sv index 901d05d..1dcc802 100644 --- a/src/hyperbus_synchronous.sv +++ b/src/hyperbus_synchronous.sv @@ -48,27 +48,11 @@ module hyperbus_synchronous #( `ASSERT_INIT(AxiAddrWidthValid, AxiAddrWidth >= $clog2(AxiDataWidth / 8)) - typedef struct packed { - logic [(16*NumPhys)-1:0] data; - logic last; - logic [(2*NumPhys)-1:0] strb; - } hyper_tx_t; - - typedef struct packed { - logic [(16*NumPhys)-1:0] data; - logic last; - logic error; - } hyper_rx_t; - - typedef struct packed { - hyperbus_pkg::hyper_tf_t trans; - logic [NumChips-1:0] cs; - } hyper_cmd_t; - typedef logic [AxiAddrWidth-1:0] host_addr_t; typedef logic [AxiDataWidth-1:0] host_data_t; typedef logic [AxiDataWidth/8-1:0] host_strb_t; `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) + `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys, NumChips) logic clk_backend; logic clk_backend_90; @@ -88,45 +72,13 @@ module hyperbus_synchronous #( axi_rule_t [NumChips-1:0] frontend_chip_rules; host_req_t host_req; - logic host_req_valid; - logic host_req_ready; - host_w_t host_w; - logic host_w_valid; - logic host_w_ready; - host_r_t host_r; - logic host_r_valid; - logic host_r_ready; - host_wrsp_t host_wrsp; - logic host_wrsp_valid; - logic host_wrsp_ready; - - hyper_rx_t midend_rx; - logic midend_rx_valid; - logic midend_rx_ready; - hyper_tx_t midend_tx; - logic midend_tx_valid; - logic midend_tx_ready; - logic midend_wrsp_error; - logic midend_wrsp_valid; - logic midend_wrsp_ready; - hyper_cmd_t midend_cmd; - logic midend_cmd_valid; - logic midend_cmd_ready; + host_rsp_t host_rsp; + hyper_req_t midend_req; + hyper_rsp_t midend_rsp; logic midend_trans_active; logic midend_decode_error; - - hyper_rx_t backend_rx; - logic backend_rx_valid; - logic backend_rx_ready; - hyper_tx_t backend_tx; - logic backend_tx_valid; - logic backend_tx_ready; - logic backend_wrsp_error; - logic backend_wrsp_valid; - logic backend_wrsp_ready; - hyper_cmd_t backend_cmd; - logic backend_cmd_valid; - logic backend_cmd_ready; + hyper_req_t backend_req; + hyper_rsp_t backend_rsp; assign clk_backend = clk_sys_i; assign rst_backend_n = rst_sys_ni; @@ -137,21 +89,8 @@ module hyperbus_synchronous #( assign frontend_cfg_apply_ready = backend_cfg_apply_ready; assign frontend_cfg_apply_done = frontend_cfg_apply_valid && backend_cfg_apply_ready; - assign midend_rx = backend_rx; - assign midend_rx_valid = backend_rx_valid; - assign backend_rx_ready = midend_rx_ready; - - assign backend_tx = midend_tx; - assign backend_tx_valid = midend_tx_valid; - assign midend_tx_ready = backend_tx_ready; - - assign midend_wrsp_error = backend_wrsp_error; - assign midend_wrsp_valid = backend_wrsp_valid; - assign backend_wrsp_ready = midend_wrsp_ready; - - assign backend_cmd = midend_cmd; - assign backend_cmd_valid = midend_cmd_valid; - assign midend_cmd_ready = backend_cmd_ready; + assign backend_req = midend_req; + assign midend_rsp = backend_rsp; hyperbus_tx_clk_delay i_tx_clk_delay ( .rst_ni ( rst_backend_n ), @@ -196,9 +135,7 @@ module hyperbus_synchronous #( .axi_req_t ( axi_req_t ), .axi_rsp_t ( axi_rsp_t ), .host_req_t ( host_req_t ), - .host_w_t ( host_w_t ), - .host_r_t ( host_r_t ), - .host_wrsp_t ( host_wrsp_t ) + .host_rsp_t ( host_rsp_t ) ) i_axi_frontend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), @@ -206,18 +143,8 @@ module hyperbus_synchronous #( .idle_o ( host_idle ), .axi_req_i ( axi_req_i ), .axi_rsp_o ( axi_rsp_o ), - .host_req_o ( host_req ), - .host_req_valid_o ( host_req_valid ), - .host_req_ready_i ( host_req_ready ), - .host_w_o ( host_w ), - .host_w_valid_o ( host_w_valid ), - .host_w_ready_i ( host_w_ready ), - .host_r_i ( host_r ), - .host_r_valid_i ( host_r_valid ), - .host_r_ready_o ( host_r_ready ), - .host_wrsp_i ( host_wrsp ), - .host_wrsp_valid_i ( host_wrsp_valid ), - .host_wrsp_ready_o ( host_wrsp_ready ) + .host_req_o ( host_req ), + .host_rsp_i ( host_rsp ) ); hyperbus_midend #( @@ -225,45 +152,29 @@ module hyperbus_synchronous #( .HostDataWidth ( AxiDataWidth ), .NumChips ( NumChips ), .NumPhys ( NumPhys ), - .host_req_t ( host_req_t ), + .host_cmd_t ( host_cmd_t ), .host_w_t ( host_w_t ), .host_r_t ( host_r_t ), .host_wrsp_t ( host_wrsp_t ), + .host_req_t ( host_req_t ), + .host_rsp_t ( host_rsp_t ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), .hyper_cmd_t ( hyper_cmd_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ), .rule_t ( axi_rule_t ) ) i_midend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), - .host_req_i ( host_req ), - .host_req_valid_i ( host_req_valid ), - .host_req_ready_o ( host_req_ready ), - .host_w_i ( host_w ), - .host_w_valid_i ( host_w_valid ), - .host_w_ready_o ( host_w_ready ), - .host_r_o ( host_r ), - .host_r_valid_o ( host_r_valid ), - .host_r_ready_i ( host_r_ready ), - .host_wrsp_o ( host_wrsp ), - .host_wrsp_valid_o ( host_wrsp_valid ), - .host_wrsp_ready_i ( host_wrsp_ready ), + .host_link_req_i ( host_req ), + .host_link_rsp_o ( host_rsp ), .frontend_cfg_i ( frontend_cfg ), .chip_rules_i ( frontend_chip_rules ), .trans_active_o ( midend_trans_active ), .decode_error_o ( midend_decode_error ), - .rx_i ( midend_rx ), - .rx_valid_i ( midend_rx_valid ), - .rx_ready_o ( midend_rx_ready ), - .tx_o ( midend_tx ), - .tx_valid_o ( midend_tx_valid ), - .tx_ready_i ( midend_tx_ready ), - .wrsp_error_i ( midend_wrsp_error ), - .wrsp_valid_i ( midend_wrsp_valid ), - .wrsp_ready_o ( midend_wrsp_ready ), - .cmd_o ( midend_cmd ), - .cmd_valid_o ( midend_cmd_valid ), - .cmd_ready_i ( midend_cmd_ready ) + .hyper_link_req_o ( midend_req ), + .hyper_link_rsp_i ( midend_rsp ) ); hyperbus_backend #( @@ -273,7 +184,8 @@ module hyperbus_synchronous #( .SyncStages ( SyncStages ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), - .hyper_cmd_t ( hyper_cmd_t ) + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ) ) i_backend ( .clk_i ( clk_backend ), .clk_90_i ( clk_backend_90 ), @@ -284,18 +196,8 @@ module hyperbus_synchronous #( .cfg_apply_ready_o ( backend_cfg_apply_ready ), .busy_o ( ), .tx_clk_delay_o ( backend_tx_clk_delay ), - .rx_o ( backend_rx ), - .rx_valid_o ( backend_rx_valid ), - .rx_ready_i ( backend_rx_ready ), - .tx_i ( backend_tx ), - .tx_valid_i ( backend_tx_valid ), - .tx_ready_o ( backend_tx_ready ), - .wrsp_error_o ( backend_wrsp_error ), - .wrsp_valid_o ( backend_wrsp_valid ), - .wrsp_ready_i ( backend_wrsp_ready ), - .cmd_i ( backend_cmd ), - .cmd_valid_i ( backend_cmd_valid ), - .cmd_ready_o ( backend_cmd_ready ), + .req_i ( backend_req ), + .rsp_o ( backend_rsp ), .hyper_cs_no ( hyper_cs_no ), .hyper_ck_o ( hyper_ck_o ), .hyper_ck_no ( hyper_ck_no ), diff --git a/src/hyperbus_w2phy.sv b/src/hyperbus_write_adapter.sv similarity index 70% rename from src/hyperbus_w2phy.sv rename to src/hyperbus_write_adapter.sv index 681dfd0..65b9fc3 100644 --- a/src/hyperbus_w2phy.sv +++ b/src/hyperbus_write_adapter.sv @@ -8,7 +8,7 @@ `include "common_cells/registers.svh" -module hyperbus_w2phy #( +module hyperbus_write_adapter #( parameter int unsigned HostDataWidth = -1, parameter int unsigned NumPhys = -1, parameter type T = logic, @@ -18,8 +18,8 @@ module hyperbus_w2phy #( input logic rst_ni, input logic [2:0] size_i, input logic [AddrWidth-1:0] start_addr_i, - input logic is_write_i, - input logic cmd_fire_i, + input logic start_i, + input logic dual_phy_i, input logic host_valid_i, output logic host_ready_o, input T data_i, @@ -40,16 +40,16 @@ module hyperbus_w2phy #( Idle, Sample, CntReady - } hyper_upsizer_state_e; + } write_adapter_state_e; typedef struct packed { logic [HostDataWidth/8-1:0] strb; logic [HostDataWidth-1:0] data; logic last; - } w2phy_chan_t; + } write_buffer_t; - hyper_upsizer_state_e state_d, state_q; - w2phy_chan_t data_buffer_d, data_buffer_q; + write_adapter_state_e state_d, state_q; + write_buffer_t data_buffer_d, data_buffer_q; logic upsize; logic enough_data; logic first_tx_d, first_tx_q; @@ -59,6 +59,11 @@ module hyperbus_w2phy #( logic [3:0] size_d, size_q; logic [AddrWidth-1:0] cnt_data_phy_d, cnt_data_phy_q; logic keep_sending; + logic [16*NumPhys-1:0] converted_data; + logic [2*NumPhys-1:0] converted_strb; + logic converted_last; + logic converted_valid; + logic converted_ready; assign upsize = ((size_q == 1) && (NumPhys == 2)) || (size_q == 0); assign enough_data = !upsize; @@ -66,10 +71,10 @@ module hyperbus_w2phy #( (cnt_data_phy_d != byte_idx_q); assign word_cnt = cnt_data_phy_q >> ($clog2(NumPhys) + 1); - assign data_o = data_buffer_q.data[(16*NumPhys)*word_cnt +: (16*NumPhys)]; - assign strb_o = data_buffer_q.strb[(2*NumPhys)*word_cnt +: (2*NumPhys)] & - mask_strobe_q; - assign last_o = data_buffer_q.last && (!keep_sending || upsize); + assign converted_data = data_buffer_q.data[(16*NumPhys)*word_cnt +: (16*NumPhys)]; + assign converted_strb = data_buffer_q.strb[(2*NumPhys)*word_cnt +: (2*NumPhys)] & + mask_strobe_q; + assign converted_last = data_buffer_q.last && (!keep_sending || upsize); always_comb begin : proc_counters byte_idx_d = byte_idx_q; @@ -77,7 +82,7 @@ module hyperbus_w2phy #( cnt_data_phy_d = cnt_data_phy_q; first_tx_d = first_tx_q; - if (cmd_fire_i && is_write_i) begin + if (start_i) begin byte_idx_d = start_addr_i; size_d = size_i; cnt_data_phy_d = (start_addr_i >> NumPhys) << NumPhys; @@ -87,7 +92,7 @@ module hyperbus_w2phy #( byte_idx_d = ((byte_idx_q >> size_d) << size_d) + (1 << size_d); first_tx_d = 1'b0; end - if (phy_valid_o && phy_ready_i) begin + if (converted_valid && converted_ready) begin cnt_data_phy_d = cnt_data_phy_q + NumPhys * 2; end end @@ -128,12 +133,12 @@ module hyperbus_w2phy #( state_d = state_q; mask_strobe_d = mask_strobe_q; host_ready_o = 1'b0; - phy_valid_o = 1'b0; + converted_valid = 1'b0; unique case (state_q) Idle: begin mask_strobe_d = '1; - if (cmd_fire_i && is_write_i) begin + if (start_i) begin state_d = Sample; end end @@ -155,10 +160,10 @@ module hyperbus_w2phy #( end end CntReady: begin - phy_valid_o = 1'b1; - if (phy_ready_i) begin - if (last_o) begin - state_d = cmd_fire_i ? Sample : Idle; + converted_valid = 1'b1; + if (converted_ready) begin + if (converted_last) begin + state_d = start_i ? Sample : Idle; end else if (size_d >= NumPhys) begin if (cnt_data_phy_d != byte_idx_q) begin state_d = CntReady; @@ -180,6 +185,46 @@ module hyperbus_w2phy #( endcase end + if (NumPhys == 2) begin : gen_dual_phy + logic split_d, split_q; + + always_comb begin : proc_phy_width + data_o = converted_data; + strb_o = converted_strb; + last_o = converted_last; + phy_valid_o = converted_valid; + converted_ready = phy_ready_i; + split_d = split_q; + + if (!dual_phy_i) begin + data_o = {converted_data[15:0], converted_data[15:0]}; + strb_o = {converted_strb[1:0], converted_strb[1:0]}; + last_o = converted_last && split_q; + converted_ready = phy_ready_i && split_q; + if (split_q) begin + data_o = {converted_data[31:16], converted_data[31:16]}; + strb_o = {converted_strb[3:2], converted_strb[3:2]}; + end + if (phy_valid_o && phy_ready_i) begin + split_d = !split_q; + end + end + if (start_i) begin + split_d = 1'b0; + end + end + + `FFARN(split_q, split_d, 1'b0, clk_i, rst_ni) + end else begin : gen_single_phy + always_comb begin : proc_phy_width + data_o = converted_data; + strb_o = converted_strb; + last_o = converted_last; + phy_valid_o = converted_valid; + converted_ready = phy_ready_i; + end + end + `FFARN(state_q, state_d, Idle, clk_i, rst_ni) `FFARN(data_buffer_q, data_buffer_d, '0, clk_i, rst_ni) `FFARN(byte_idx_q, byte_idx_d, '0, clk_i, rst_ni) @@ -187,5 +232,4 @@ module hyperbus_w2phy #( `FFARN(cnt_data_phy_q, cnt_data_phy_d, '0, clk_i, rst_ni) `FFARN(first_tx_q, first_tx_d, 1'b0, clk_i, rst_ni) `FFARN(mask_strobe_q, mask_strobe_d, '0, clk_i, rst_ni) - -endmodule +endmodule : hyperbus_write_adapter From b30b46a3e88e03e364262c359577138186406399 Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Thu, 23 Jul 2026 17:19:11 +0200 Subject: [PATCH 09/11] Centralize HyperBus backend and clock generation Group PHY-specific RTL under the backend, place transmit delay generation beside the PHY, and use one configurable clock-generation path for all top-level variants. Convert parameter and protocol checks to common_cells assertion macros. --- Bender.yml | 15 ++-- docs/regs/hyperbus_cfg_regs.md | 19 ++++- include/hyperbus_cfg_regs.h | 9 +- src/{ => backend}/hyperbus_backend.sv | 22 +++-- src/{ => backend}/hyperbus_clock_diff_out.sv | 0 src/{ => backend}/hyperbus_ddr_out.sv | 0 src/{ => backend}/hyperbus_delay.sv | 0 src/{ => backend}/hyperbus_phy.sv | 4 +- src/{ => backend}/hyperbus_trx.sv | 27 ++---- src/{ => backend}/hyperbus_tx_clk_delay.sv | 0 src/hyperbus_asynchronous.sv | 32 +++----- src/hyperbus_axi.sv | 24 ++---- src/hyperbus_cfg_frontend.sv | 59 +++++++++++--- src/hyperbus_cfg_regs.sv | 3 +- src/hyperbus_clk_gen.sv | 61 -------------- src/hyperbus_isochronous.sv | 86 ++++++++++++++++---- src/hyperbus_pkg.sv | 1 + src/hyperbus_synchronous.sv | 24 ++---- src/regs/hyperbus_cfg_regblock.sv | 39 +++++++++ src/regs/hyperbus_cfg_regblock_pkg.sv | 11 ++- src/regs/hyperbus_cfg_regs.rdl | 11 +++ test/axi_hyper_tb.sv | 53 ++++++++++-- test/hyperbus_cfg_regs_tb.sv | 12 +++ 23 files changed, 318 insertions(+), 194 deletions(-) rename src/{ => backend}/hyperbus_backend.sv (95%) rename src/{ => backend}/hyperbus_clock_diff_out.sv (100%) rename src/{ => backend}/hyperbus_ddr_out.sv (100%) rename src/{ => backend}/hyperbus_delay.sv (100%) rename src/{ => backend}/hyperbus_phy.sv (99%) rename src/{ => backend}/hyperbus_trx.sv (89%) rename src/{ => backend}/hyperbus_tx_clk_delay.sv (100%) delete mode 100644 src/hyperbus_clk_gen.sv diff --git a/Bender.yml b/Bender.yml index 18967ea..c074308 100644 --- a/Bender.yml +++ b/Bender.yml @@ -42,27 +42,26 @@ sources: - target: any(test,all(not(hyperbus_exclude_dline),not(all(xilinx,hyperbus_dline)))) files: - - src/hyperbus_delay.sv + - src/backend/hyperbus_delay.sv - src/regs/hyperbus_cfg_regblock_pkg.sv - - src/hyperbus_tx_clk_delay.sv - src/hyperbus_pkg.sv - - src/hyperbus_clk_gen.sv - - src/hyperbus_clock_diff_out.sv + - src/backend/hyperbus_tx_clk_delay.sv + - src/backend/hyperbus_clock_diff_out.sv - src/hyperbus_write_adapter.sv - src/hyperbus_read_adapter.sv - - src/hyperbus_ddr_out.sv - - src/hyperbus_trx.sv + - src/backend/hyperbus_ddr_out.sv + - src/backend/hyperbus_trx.sv - src/regs/hyperbus_cfg_regblock.sv - src/hyperbus_cfg_regs.sv - - src/hyperbus_phy.sv + - src/backend/hyperbus_phy.sv - src/hyperbus_axi.sv - src/hyperbus_atomic_handler.sv - src/hyperbus_midend.sv - src/hyperbus_cfg_frontend.sv - src/hyperbus_async_bridge.sv - src/hyperbus_iso_bridge.sv - - src/hyperbus_backend.sv + - src/backend/hyperbus_backend.sv - src/hyperbus_synchronous.sv - src/hyperbus_asynchronous.sv - src/hyperbus_isochronous.sv diff --git a/docs/regs/hyperbus_cfg_regs.md b/docs/regs/hyperbus_cfg_regs.md index 7d77eaa..4a6b384 100644 --- a/docs/regs/hyperbus_cfg_regs.md +++ b/docs/regs/hyperbus_cfg_regs.md @@ -9,7 +9,7 @@ Don't override. Generated from: hyperbus_cfg_regs - Absolute Address: 0x0 - Base Offset: 0x0 -- Size: 0x78 +- Size: 0x7C

HyperBus controller configuration. Chip address ranges must be 4 MiB aligned, ordered by chip-select index, and non-overlapping.

@@ -44,6 +44,7 @@ Don't override. Generated from: hyperbus_cfg_regs | 0x6C | chip6_bound | — | | 0x70 | chip7_base | — | | 0x74 | chip7_bound | — | +| 0x78 | phy_clock_div | — | ### t_latency_access register @@ -418,3 +419,19 @@ Don't override. Generated from: hyperbus_cfg_regs | Bits|Identifier|Access|Reset|Name| |-----|----------|------|-----|----| |31:22| value | rw | 0x8 | — | + +### phy_clock_div register + +- Absolute Address: 0x78 +- Base Offset: 0x78 +- Size: 0x4 + +

System-to-PHY clock division factor used by the isochronous top.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| value | rw | 0x8 | — | + +#### value field + +

Must be at least two. Ignored by synchronous and asynchronous tops.

diff --git a/include/hyperbus_cfg_regs.h b/include/hyperbus_cfg_regs.h index 80dd478..9e8a234 100644 --- a/include/hyperbus_cfg_regs.h +++ b/include/hyperbus_cfg_regs.h @@ -191,6 +191,12 @@ extern "C" { #define HYPERBUS_CFG_REGS__CHIP7_BOUND__VALUE_bw 10 #define HYPERBUS_CFG_REGS__CHIP7_BOUND__VALUE_reset 0x8 +// reg - hyperbus_cfg_regs::phy_clock_div +#define HYPERBUS_CFG_REGS__PHY_CLOCK_DIV__VALUE_bm 0xff +#define HYPERBUS_CFG_REGS__PHY_CLOCK_DIV__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__PHY_CLOCK_DIV__VALUE_bw 8 +#define HYPERBUS_CFG_REGS__PHY_CLOCK_DIV__VALUE_reset 0x8 + // addrmap - hyperbus_cfg_regs typedef struct __attribute__ ((__packed__)) { uint32_t t_latency_access; @@ -223,10 +229,11 @@ typedef struct __attribute__ ((__packed__)) { uint32_t chip6_bound; uint32_t chip7_base; uint32_t chip7_bound; + uint32_t phy_clock_div; } hyperbus_cfg_regs_t; -static_assert(sizeof(hyperbus_cfg_regs_t) == 0x78, "Packing error"); +static_assert(sizeof(hyperbus_cfg_regs_t) == 0x7c, "Packing error"); #ifdef __cplusplus } diff --git a/src/hyperbus_backend.sv b/src/backend/hyperbus_backend.sv similarity index 95% rename from src/hyperbus_backend.sv rename to src/backend/hyperbus_backend.sv index 7d40185..f7349c8 100644 --- a/src/hyperbus_backend.sv +++ b/src/backend/hyperbus_backend.sv @@ -16,8 +16,10 @@ module hyperbus_backend #( parameter type hyper_rsp_t = logic ) ( input logic clk_i, - input logic clk_90_i, input logic rst_ni, +`ifdef TARGET_XILINX + input logic clk_ref200_i, +`endif input logic test_mode_i, input hyperbus_pkg::phy_cfg_t cfg_apply_i, @@ -25,7 +27,6 @@ module hyperbus_backend #( output logic cfg_apply_ready_o, output logic busy_o, - output logic [7:0] tx_clk_delay_o, input hyper_req_t req_i, output hyper_rsp_t rsp_o, @@ -45,6 +46,7 @@ module hyperbus_backend #( hyperbus_pkg::phy_cfg_t cfg_q; logic cfg_apply_accepted; logic phy_busy_any; + logic clk_tx; `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= 8) `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) @@ -53,10 +55,20 @@ module hyperbus_backend #( assign cfg_apply_ready_o = ~phy_busy_any; assign cfg_apply_accepted = cfg_apply_valid_i && cfg_apply_ready_o; assign busy_o = phy_busy_any || cfg_apply_valid_i; - assign tx_clk_delay_o = cfg_q.t_tx_clk_delay; `FFLARN(cfg_q, cfg_apply_i, cfg_apply_accepted, '0, clk_i, rst_ni) + hyperbus_tx_clk_delay i_tx_clk_delay ( + .rst_ni, +`ifdef TARGET_XILINX + .clk_ref200_i, +`endif + .clk_i, + .in_i ( clk_i ), + .delay_i ( cfg_q.t_tx_clk_delay ), + .out_o ( clk_tx ) + ); + if (NumPhys == 2) begin : gen_dual_phy hyperbus_pkg::phy_rx_t [NumPhys-1:0] phy_rx; hyperbus_pkg::phy_rx_t [NumPhys-1:0] buffered_rx; @@ -163,7 +175,7 @@ module hyperbus_backend #( .SyncStages ( SyncStages ) ) i_phy ( .clk_i, - .clk_i_90 ( clk_90_i ), + .clk_tx_i ( clk_tx ), .rst_ni, .test_mode_i, .cfg_i ( cfg_q ), @@ -205,7 +217,7 @@ module hyperbus_backend #( .SyncStages ( SyncStages ) ) i_phy ( .clk_i, - .clk_i_90 ( clk_90_i ), + .clk_tx_i ( clk_tx ), .rst_ni, .test_mode_i, .cfg_i ( cfg_q ), diff --git a/src/hyperbus_clock_diff_out.sv b/src/backend/hyperbus_clock_diff_out.sv similarity index 100% rename from src/hyperbus_clock_diff_out.sv rename to src/backend/hyperbus_clock_diff_out.sv diff --git a/src/hyperbus_ddr_out.sv b/src/backend/hyperbus_ddr_out.sv similarity index 100% rename from src/hyperbus_ddr_out.sv rename to src/backend/hyperbus_ddr_out.sv diff --git a/src/hyperbus_delay.sv b/src/backend/hyperbus_delay.sv similarity index 100% rename from src/hyperbus_delay.sv rename to src/backend/hyperbus_delay.sv diff --git a/src/hyperbus_phy.sv b/src/backend/hyperbus_phy.sv similarity index 99% rename from src/hyperbus_phy.sv rename to src/backend/hyperbus_phy.sv index 802d7ff..c531e67 100644 --- a/src/hyperbus_phy.sv +++ b/src/backend/hyperbus_phy.sv @@ -18,7 +18,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( parameter int unsigned StartupCycles = 300 /*us*/ * 200 /*MHz*/ // Conservative maximum frequency estimate )( input logic clk_i, - input logic clk_i_90, + input logic clk_tx_i, input logic rst_ni, input logic test_mode_i, // Config registers @@ -130,7 +130,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( .SyncStages ( SyncStages ) ) i_trx ( .clk_i, - .clk_i_90, + .clk_tx_i, .rst_ni, .test_mode_i, .cs_i ( cs_q ), diff --git a/src/hyperbus_trx.sv b/src/backend/hyperbus_trx.sv similarity index 89% rename from src/hyperbus_trx.sv rename to src/backend/hyperbus_trx.sv index 7ca194c..73fc128 100644 --- a/src/hyperbus_trx.sv +++ b/src/backend/hyperbus_trx.sv @@ -6,6 +6,8 @@ // Armin Berger // Stephan Keck +`include "common_cells/assertions.svh" + module hyperbus_trx #( parameter int unsigned NumChips = 2, parameter int unsigned RxFifoLogDepth = 3, @@ -13,7 +15,7 @@ module hyperbus_trx #( )( // Global signals input logic clk_i, - input logic clk_i_90, + input logic clk_tx_i, input logic rst_ni, input logic test_mode_i, @@ -48,9 +50,7 @@ module hyperbus_trx #( output logic hyper_reset_no ); - // 90-degree-shifted clocks generated with delay line logic tx_clk_ena_q; - logic tx_clk_90; logic rx_rwds_90; // Delayed clock enable synchronous with data @@ -72,14 +72,10 @@ module hyperbus_trx #( // TX + control // ================= - // Shift clock by 90 degrees - assign tx_clk_90 = clk_i_90; - - // 90deg-shifted differential output clock, sampling output bytes centrally - // TODO: tx_clk_ena_q to tx_clk_90 may need a constraint at the pins of this module - // specifically tx_clk_ena_q must arrive BEFORE tx_clk_90 otherwise the gating may fail + // The delayed differential output clock samples output bytes centrally. + // TODO: tx_clk_ena_q must arrive before clk_tx_i to avoid disturbing clock gating. hyperbus_clock_diff_out i_clock_diff_out ( - .in_i ( tx_clk_90 ), + .in_i ( clk_tx_i ), .en_i ( tx_clk_ena_q ), .out_o ( hyper_ck_o ), .out_no ( hyper_ck_no ) @@ -228,14 +224,7 @@ module hyperbus_trx #( .dst_ready_i ( rx_ready_i ) ); - // assert that the FIFO does not drop data in simulation - `ifndef SYNTHESIS - always @(negedge rx_rwds_fifo_ready) assert(rx_rwds_fifo_ready) - else $error("%m: HyperBus RX FIFO must always be ready to receive data"); - - rx_capture_control_exclusive : assert property ( - @(posedge clk_i) disable iff (!rst_ni) !(rx_clk_set_i && rx_clk_reset_i) - ) else $error("%m: RWDS capture set and reset asserted together"); - `endif + `ASSERT(RxRwdsFifoReady, rx_rwds_fifo_ready, rx_rwds_clk_n, !rst_ni) + `ASSERT(RxCaptureControlExclusive, !(rx_clk_set_i && rx_clk_reset_i)) endmodule diff --git a/src/hyperbus_tx_clk_delay.sv b/src/backend/hyperbus_tx_clk_delay.sv similarity index 100% rename from src/hyperbus_tx_clk_delay.sv rename to src/backend/hyperbus_tx_clk_delay.sv diff --git a/src/hyperbus_asynchronous.sv b/src/hyperbus_asynchronous.sv index ab26d99..376890a 100644 --- a/src/hyperbus_asynchronous.sv +++ b/src/hyperbus_asynchronous.sv @@ -60,13 +60,12 @@ module hyperbus_asynchronous #( `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys, NumChips) logic clk_backend; - logic clk_backend_90; logic rst_backend_n; - logic [7:0] backend_tx_clk_delay; hyperbus_pkg::phy_cfg_t frontend_cfg_apply; logic frontend_cfg_apply_valid; logic frontend_cfg_apply_ready; logic frontend_cfg_apply_done; + logic frontend_clock_div_apply_valid; logic frontend_drain; logic host_idle; hyperbus_pkg::phy_cfg_t backend_cfg_apply; @@ -102,17 +101,6 @@ module hyperbus_asynchronous #( .init_no ( ) ); - hyperbus_tx_clk_delay i_tx_clk_delay ( - .rst_ni ( rst_backend_n ), -`ifdef TARGET_XILINX - .clk_ref200_i ( clk_ref200_i ), -`endif - .clk_i ( clk_backend ), - .in_i ( clk_backend ), - .delay_i ( backend_tx_clk_delay ), - .out_o ( clk_backend_90 ) - ); - hyperbus_cfg_frontend #( .NumChips ( NumChips ), .NumPhys ( NumPhys ), @@ -132,6 +120,10 @@ module hyperbus_asynchronous #( .cfg_apply_valid_o ( frontend_cfg_apply_valid ), .cfg_apply_ready_i ( frontend_cfg_apply_ready ), .cfg_apply_done_i ( frontend_cfg_apply_done ), + .clock_div_apply_o ( ), + .clock_div_apply_valid_o ( frontend_clock_div_apply_valid ), + .clock_div_apply_ready_i ( 1'b1 ), + .clock_div_apply_done_i ( frontend_clock_div_apply_valid ), .frontend_cfg_o ( frontend_cfg ), .chip_rules_o ( frontend_chip_rules ), .decode_error_i ( midend_decode_error ) @@ -226,14 +218,15 @@ module hyperbus_asynchronous #( .hyper_rsp_t ( hyper_rsp_t ) ) i_backend ( .clk_i ( clk_backend ), - .clk_90_i ( clk_backend_90 ), .rst_ni ( rst_backend_n ), +`ifdef TARGET_XILINX + .clk_ref200_i ( clk_ref200_i ), +`endif .test_mode_i ( test_mode_i ), .cfg_apply_i ( backend_cfg_apply ), .cfg_apply_valid_i ( backend_cfg_apply_valid ), .cfg_apply_ready_o ( backend_cfg_apply_ready ), .busy_o ( ), - .tx_clk_delay_o ( backend_tx_clk_delay ), .req_i ( backend_req ), .rsp_o ( backend_rsp ), .hyper_cs_no ( hyper_cs_no ), @@ -248,12 +241,7 @@ module hyperbus_asynchronous #( .hyper_reset_no ( hyper_reset_no ) ); - // pragma translate_off - `ifndef VERILATOR - phy_reset_only_when_idle : assert property ( - @(posedge clk_sys_i) disable iff (!rst_sys_ni) !rst_phy_ni |-> !midend_trans_active - ) else $error("%m: PHY reset asserted while a HyperBus transaction is active"); - `endif - // pragma translate_on + `ASSERT(PhyResetOnlyWhenIdle, !rst_phy_ni |-> !midend_trans_active, + clk_sys_i, !rst_sys_ni) endmodule diff --git a/src/hyperbus_axi.sv b/src/hyperbus_axi.sv index b45ee61..74282b5 100644 --- a/src/hyperbus_axi.sv +++ b/src/hyperbus_axi.sv @@ -365,24 +365,10 @@ module hyperbus_axi_frontend #( assign ser_out_rsp.b_valid = host_rsp_i.wrsp_valid; assign host_req_o.wrsp_ready = ser_out_req.b_ready; - // pragma translate_off - `ifndef VERILATOR - axi_burst_type : assert property( - @(posedge clk_i) host_req_o.cmd_valid && host_rsp_i.cmd_ready |-> - ((rr_out_req_ax.burst == axi_pkg::BURST_INCR) || - ((rr_out_req_ax.burst == axi_pkg::BURST_FIXED) && (rr_out_req_ax.len == '0)))) - else $fatal(1, "Only incremental bursts and single-beat fixed bursts are supported."); - - read_response_pending : assert property( - @(posedge clk_i) disable iff (!rst_ni) - axi_r_completed |-> (read_pending_q != '0)) - else $fatal(1, "AXI read response completed without a pending request."); - - write_response_pending : assert property( - @(posedge clk_i) disable iff (!rst_ni) - axi_b_accepted |-> (write_pending_q != '0)) - else $fatal(1, "AXI write response completed without a pending request."); - `endif - // pragma translate_on + `ASSERT(AxiBurstType, (host_req_o.cmd_valid && host_rsp_i.cmd_ready) |-> + ((rr_out_req_ax.burst == axi_pkg::BURST_INCR) || + ((rr_out_req_ax.burst == axi_pkg::BURST_FIXED) && (rr_out_req_ax.len == '0)))) + `ASSERT(ReadResponsePending, axi_r_completed |-> (read_pending_q != '0)) + `ASSERT(WriteResponsePending, axi_b_accepted |-> (write_pending_q != '0)) endmodule diff --git a/src/hyperbus_cfg_frontend.sv b/src/hyperbus_cfg_frontend.sv index 4ca88ea..3465bbc 100644 --- a/src/hyperbus_cfg_frontend.sv +++ b/src/hyperbus_cfg_frontend.sv @@ -28,6 +28,11 @@ module hyperbus_cfg_frontend #( input logic cfg_apply_ready_i, input logic cfg_apply_done_i, + output logic [7:0] clock_div_apply_o, + output logic clock_div_apply_valid_o, + input logic clock_div_apply_ready_i, + input logic clock_div_apply_done_i, + output hyperbus_pkg::frontend_cfg_t frontend_cfg_o, output host_rule_t [NumChips-1:0] chip_rules_o, input logic decode_error_i @@ -38,8 +43,10 @@ module hyperbus_cfg_frontend #( CfgDrain, CfgCommit, CfgObserve, - CfgSend, - CfgWaitAck + CfgClockSend, + CfgClockWaitAck, + CfgPhySend, + CfgPhyWaitAck } cfg_state_e; cfg_state_e cfg_state_d; @@ -47,6 +54,10 @@ module hyperbus_cfg_frontend #( hyperbus_pkg::phy_cfg_t phy_cfg; hyperbus_pkg::phy_cfg_t cfg_applied_d; hyperbus_pkg::phy_cfg_t cfg_applied_q; + logic [7:0] clock_div_applied_d; + logic [7:0] clock_div_applied_q; + logic phy_cfg_changed; + logic clock_div_changed; logic cfg_changed; logic cfg_write_pending_d; logic cfg_write_pending_q; @@ -58,10 +69,14 @@ module hyperbus_cfg_frontend #( reg_req_t cfg_reg_req; reg_rsp_t cfg_reg_rsp; - assign cfg_changed = phy_cfg != cfg_applied_q; - assign drain_o = (cfg_state_q != CfgIdle) || cfg_changed; - assign cfg_apply_valid_o = cfg_state_q == CfgSend; - assign cfg_apply_o = phy_cfg; + assign phy_cfg_changed = phy_cfg != cfg_applied_q; + assign clock_div_changed = frontend_cfg_o.phy_clock_div != clock_div_applied_q; + assign cfg_changed = phy_cfg_changed || clock_div_changed; + assign drain_o = (cfg_state_q != CfgIdle) || cfg_changed; + assign cfg_apply_valid_o = cfg_state_q == CfgPhySend; + assign cfg_apply_o = phy_cfg; + assign clock_div_apply_valid_o = cfg_state_q == CfgClockSend; + assign clock_div_apply_o = frontend_cfg_o.phy_clock_div; always_comb begin : proc_cfg_reg_gate cfg_reg_req = reg_req_i; @@ -85,6 +100,7 @@ module hyperbus_cfg_frontend #( always_comb begin : proc_cfg_apply cfg_state_d = cfg_state_q; cfg_applied_d = cfg_applied_q; + clock_div_applied_d = clock_div_applied_q; cfg_write_pending_d = cfg_write_pending_q; unique case (cfg_state_q) @@ -99,7 +115,7 @@ module hyperbus_cfg_frontend #( end CfgDrain: begin if (host_idle_i && !trans_active_i) begin - cfg_state_d = cfg_write_pending_q ? CfgCommit : CfgSend; + cfg_state_d = cfg_write_pending_q ? CfgCommit : CfgObserve; end end CfgCommit: begin @@ -109,23 +125,41 @@ module hyperbus_cfg_frontend #( end end CfgObserve: begin - if (cfg_changed) begin - cfg_state_d = CfgSend; + if (clock_div_changed) begin + cfg_state_d = CfgClockSend; + end else if (phy_cfg_changed) begin + cfg_state_d = CfgPhySend; end else begin cfg_state_d = CfgIdle; end end - CfgSend: begin + CfgClockSend: begin + if (clock_div_apply_ready_i) begin + if (clock_div_apply_done_i) begin + clock_div_applied_d = frontend_cfg_o.phy_clock_div; + cfg_state_d = phy_cfg_changed ? CfgPhySend : CfgIdle; + end else begin + cfg_state_d = CfgClockWaitAck; + end + end + end + CfgClockWaitAck: begin + if (clock_div_apply_done_i) begin + clock_div_applied_d = frontend_cfg_o.phy_clock_div; + cfg_state_d = phy_cfg_changed ? CfgPhySend : CfgIdle; + end + end + CfgPhySend: begin if (cfg_apply_ready_i) begin if (cfg_apply_done_i) begin cfg_applied_d = phy_cfg; cfg_state_d = CfgIdle; end else begin - cfg_state_d = CfgWaitAck; + cfg_state_d = CfgPhyWaitAck; end end end - CfgWaitAck: begin + CfgPhyWaitAck: begin if (cfg_apply_done_i) begin cfg_applied_d = phy_cfg; cfg_state_d = CfgIdle; @@ -139,6 +173,7 @@ module hyperbus_cfg_frontend #( `FFARN(cfg_state_q, cfg_state_d, CfgIdle, clk_i, rst_ni) `FFARN(cfg_applied_q, cfg_applied_d, '0, clk_i, rst_ni) + `FFARN(clock_div_applied_q, clock_div_applied_d, '0, clk_i, rst_ni) `FFARN(cfg_write_pending_q, cfg_write_pending_d, 1'b0, clk_i, rst_ni) hyperbus_cfg_regs #( diff --git a/src/hyperbus_cfg_regs.sv b/src/hyperbus_cfg_regs.sv index 18831a0..778cf68 100644 --- a/src/hyperbus_cfg_regs.sv +++ b/src/hyperbus_cfg_regs.sv @@ -26,7 +26,7 @@ module hyperbus_cfg_regs #( input decode_error_i ); localparam int unsigned NumChipsMax = 8; - localparam int unsigned NumRegs = 30; + localparam int unsigned NumRegs = 31; localparam int unsigned RegsBits = cf_math_pkg::idx_width(NumRegs); localparam int unsigned RegStrbWidth = RegDataWidth/8; @@ -192,6 +192,7 @@ module hyperbus_cfg_regs #( frontend_cfg_o.address_mask_msb = cfg_hwif_out.address_mask_msb.value.value; frontend_cfg_o.address_space = cfg_hwif_out.address_space.value.value; frontend_cfg_o.dual_phy = (NumPhys == 2) && cfg_hwif_out.dual_phy.value.value; + frontend_cfg_o.phy_clock_div = cfg_hwif_out.phy_clock_div.value.value; phy_cfg_o.chip.t_latency_access = cfg_hwif_out.t_latency_access.value.value; phy_cfg_o.chip.en_latency_additional = cfg_hwif_out.en_latency_additional.value.value; diff --git a/src/hyperbus_clk_gen.sv b/src/hyperbus_clk_gen.sv deleted file mode 100644 index 26400c8..0000000 --- a/src/hyperbus_clk_gen.sv +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2023 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 -// -// Hayate Okuhara - -/// Generates 4 phase shifted clocks out of one faster clock -(* no_ungroup *) -(* no_boundary_optimization *) -(* keep_hierarchy = "yes" *) -module hyperbus_clk_gen ( - input logic clk_i, // input clock - input logic rst_ni, - output logic clk0_o, // have the input clock - 0deg phase shift - output logic clk90_o, // have the input clock - 90deg phase shift - output logic clk180_o, // have the input clock - 180deg phase shift - output logic clk270_o, // have the input clock - 270deg phase shift - output logic rst_no -); - - - logic r_clk0_o; - logic r_clk90_o; - logic r_clk180_o; - logic r_clk270_o; - - logic s_clk0_n; - - assign clk0_o = r_clk0_o; - assign clk90_o = r_clk90_o; - assign clk180_o = r_clk180_o; - assign clk270_o = r_clk270_o; - assign rst_no = rst_ni; - - tc_clk_inverter i_clk0_inverter ( - .clk_i (r_clk0_o), - .clk_o (s_clk0_n) - ); - - - always_ff @(posedge clk_i or negedge rst_ni) begin - if(~rst_ni) begin - r_clk0_o <= 0; - r_clk180_o <= 1; - end else begin - r_clk0_o <= s_clk0_n; - r_clk180_o <= r_clk90_o; - end - end - - always_ff @(negedge clk_i or negedge rst_ni) begin - if (~rst_ni) begin - r_clk90_o <= 0; - r_clk270_o <= 1; - end else begin - r_clk90_o <= r_clk0_o; - r_clk270_o <= r_clk180_o; - end - end - -endmodule diff --git a/src/hyperbus_isochronous.sv b/src/hyperbus_isochronous.sv index 2be9c01..a08551e 100644 --- a/src/hyperbus_isochronous.sv +++ b/src/hyperbus_isochronous.sv @@ -4,6 +4,7 @@ `include "hyperbus/typedef.svh" `include "common_cells/assertions.svh" +`include "common_cells/registers.svh" module hyperbus_isochronous #( parameter int unsigned NumChips = -1, @@ -57,18 +58,20 @@ module hyperbus_isochronous #( `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys, NumChips) logic clk_backend; - logic clk_backend_90; logic rst_backend_n; hyperbus_pkg::phy_cfg_t frontend_cfg_apply; logic frontend_cfg_apply_valid; logic frontend_cfg_apply_ready; logic frontend_cfg_apply_done; + logic [7:0] frontend_clock_div_apply; + logic frontend_clock_div_apply_valid; + logic frontend_clock_div_apply_ready; + logic frontend_clock_div_apply_done; logic frontend_drain; logic host_idle; hyperbus_pkg::phy_cfg_t backend_cfg_apply; logic backend_cfg_apply_valid; logic backend_cfg_apply_ready; - logic rst_backend_async_n; hyperbus_pkg::frontend_cfg_t frontend_cfg; axi_rule_t [NumChips-1:0] frontend_chip_rules; @@ -82,22 +85,66 @@ module hyperbus_isochronous #( hyper_req_t backend_req; hyper_rsp_t backend_rsp; - hyperbus_clk_gen i_clk_gen ( - .clk_i ( clk_sys_i ), - .rst_ni ( rst_sys_ni ), - .clk0_o ( clk_backend ), - .clk90_o ( clk_backend_90 ), - .clk180_o ( ), - .clk270_o ( ), - .rst_no ( rst_backend_async_n ) + logic clock_div_req_valid; + logic clock_div_update_accepted; + logic clock_div_update_pending_d; + logic clock_div_update_pending_q; + logic clock_alive_ready; + + assign clock_div_req_valid = frontend_clock_div_apply_valid && + clock_alive_ready && !clock_div_update_pending_q; + assign frontend_clock_div_apply_ready = clock_div_req_valid && + clock_div_update_accepted; + assign frontend_clock_div_apply_done = clock_div_update_pending_q && clock_alive_ready; + + always_comb begin : proc_clock_div_update_pending + clock_div_update_pending_d = clock_div_update_pending_q; + + if (clock_div_update_accepted) begin + clock_div_update_pending_d = 1'b1; + end else if (frontend_clock_div_apply_done) begin + clock_div_update_pending_d = 1'b0; + end + end + + `FFARN(clock_div_update_pending_q, clock_div_update_pending_d, 1'b0, + clk_sys_i, rst_sys_ni) + + clk_int_div #( + .DIV_VALUE_WIDTH ( 8 ), + .DEFAULT_DIV_VALUE ( 8 ) + ) i_clk_div ( + .clk_i ( clk_sys_i ), + .rst_ni ( rst_sys_ni ), + .en_i ( 1'b1 ), + .test_mode_en_i ( test_mode_i ), + .div_i ( frontend_clock_div_apply ), + .div_valid_i ( clock_div_req_valid ), + .div_ready_o ( clock_div_update_accepted ), + .clk_o ( clk_backend ), + .cycl_count_o ( ) ); + isochronous_4phase_handshake i_clock_alive ( + .src_clk_i ( clk_sys_i ), + .src_rst_ni ( rst_sys_ni ), + .src_valid_i ( clock_div_req_valid && clock_div_update_accepted ), + .src_ready_o ( clock_alive_ready ), + .dst_clk_i ( clk_backend ), + .dst_rst_ni ( rst_backend_n ), + .dst_valid_o ( ), + .dst_ready_i ( 1'b1 ) + ); + + `ASSERT(PhyClockDivValid, frontend_clock_div_apply_valid |-> + (frontend_clock_div_apply >= 2), clk_sys_i, !rst_sys_ni) + rstgen i_rstgen_backend ( - .clk_i ( clk_backend ), - .rst_ni ( rst_backend_async_n ), - .test_mode_i ( test_mode_i ), - .rst_no ( rst_backend_n ), - .init_no ( ) + .clk_i ( clk_backend ), + .rst_ni ( rst_sys_ni ), + .test_mode_i ( test_mode_i ), + .rst_no ( rst_backend_n ), + .init_no ( ) ); hyperbus_cfg_frontend #( @@ -119,6 +166,10 @@ module hyperbus_isochronous #( .cfg_apply_valid_o ( frontend_cfg_apply_valid ), .cfg_apply_ready_i ( frontend_cfg_apply_ready ), .cfg_apply_done_i ( frontend_cfg_apply_done ), + .clock_div_apply_o ( frontend_clock_div_apply ), + .clock_div_apply_valid_o ( frontend_clock_div_apply_valid ), + .clock_div_apply_ready_i ( frontend_clock_div_apply_ready ), + .clock_div_apply_done_i ( frontend_clock_div_apply_done ), .frontend_cfg_o ( frontend_cfg ), .chip_rules_o ( frontend_chip_rules ), .decode_error_i ( midend_decode_error ) @@ -212,14 +263,15 @@ module hyperbus_isochronous #( .hyper_rsp_t ( hyper_rsp_t ) ) i_backend ( .clk_i ( clk_backend ), - .clk_90_i ( clk_backend_90 ), .rst_ni ( rst_backend_n ), +`ifdef TARGET_XILINX + .clk_ref200_i ( clk_ref200_i ), +`endif .test_mode_i ( test_mode_i ), .cfg_apply_i ( backend_cfg_apply ), .cfg_apply_valid_i ( backend_cfg_apply_valid ), .cfg_apply_ready_o ( backend_cfg_apply_ready ), .busy_o ( ), - .tx_clk_delay_o ( ), .req_i ( backend_req ), .rsp_o ( backend_rsp ), .hyper_cs_no ( hyper_cs_no ), diff --git a/src/hyperbus_pkg.sv b/src/hyperbus_pkg.sv index 17959dd..fc85bce 100644 --- a/src/hyperbus_pkg.sv +++ b/src/hyperbus_pkg.sv @@ -52,6 +52,7 @@ package hyperbus_pkg; logic [4:0] address_mask_msb; logic address_space; logic dual_phy; + logic [7:0] phy_clock_div; } frontend_cfg_t; typedef struct packed { diff --git a/src/hyperbus_synchronous.sv b/src/hyperbus_synchronous.sv index 1dcc802..f6b48be 100644 --- a/src/hyperbus_synchronous.sv +++ b/src/hyperbus_synchronous.sv @@ -55,13 +55,12 @@ module hyperbus_synchronous #( `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys, NumChips) logic clk_backend; - logic clk_backend_90; logic rst_backend_n; - logic [7:0] backend_tx_clk_delay; hyperbus_pkg::phy_cfg_t frontend_cfg_apply; logic frontend_cfg_apply_valid; logic frontend_cfg_apply_ready; logic frontend_cfg_apply_done; + logic frontend_clock_div_apply_valid; logic frontend_drain; logic host_idle; hyperbus_pkg::phy_cfg_t backend_cfg_apply; @@ -88,21 +87,9 @@ module hyperbus_synchronous #( assign backend_cfg_apply_valid = frontend_cfg_apply_valid; assign frontend_cfg_apply_ready = backend_cfg_apply_ready; assign frontend_cfg_apply_done = frontend_cfg_apply_valid && backend_cfg_apply_ready; - assign backend_req = midend_req; assign midend_rsp = backend_rsp; - hyperbus_tx_clk_delay i_tx_clk_delay ( - .rst_ni ( rst_backend_n ), -`ifdef TARGET_XILINX - .clk_ref200_i ( clk_ref200_i ), -`endif - .clk_i ( clk_backend ), - .in_i ( clk_backend ), - .delay_i ( backend_tx_clk_delay ), - .out_o ( clk_backend_90 ) - ); - hyperbus_cfg_frontend #( .NumChips ( NumChips ), .NumPhys ( NumPhys ), @@ -122,6 +109,10 @@ module hyperbus_synchronous #( .cfg_apply_valid_o ( frontend_cfg_apply_valid ), .cfg_apply_ready_i ( frontend_cfg_apply_ready ), .cfg_apply_done_i ( frontend_cfg_apply_done ), + .clock_div_apply_o ( ), + .clock_div_apply_valid_o ( frontend_clock_div_apply_valid ), + .clock_div_apply_ready_i ( 1'b1 ), + .clock_div_apply_done_i ( frontend_clock_div_apply_valid ), .frontend_cfg_o ( frontend_cfg ), .chip_rules_o ( frontend_chip_rules ), .decode_error_i ( midend_decode_error ) @@ -188,14 +179,15 @@ module hyperbus_synchronous #( .hyper_rsp_t ( hyper_rsp_t ) ) i_backend ( .clk_i ( clk_backend ), - .clk_90_i ( clk_backend_90 ), .rst_ni ( rst_backend_n ), +`ifdef TARGET_XILINX + .clk_ref200_i ( clk_ref200_i ), +`endif .test_mode_i ( test_mode_i ), .cfg_apply_i ( backend_cfg_apply ), .cfg_apply_valid_i ( backend_cfg_apply_valid ), .cfg_apply_ready_o ( backend_cfg_apply_ready ), .busy_o ( ), - .tx_clk_delay_o ( backend_tx_clk_delay ), .req_i ( backend_req ), .rsp_o ( backend_rsp ), .hyper_cs_no ( hyper_cs_no ), diff --git a/src/regs/hyperbus_cfg_regblock.sv b/src/regs/hyperbus_cfg_regblock.sv index dd21d44..cdc9c01 100644 --- a/src/regs/hyperbus_cfg_regblock.sv +++ b/src/regs/hyperbus_cfg_regblock.sv @@ -116,6 +116,7 @@ module hyperbus_cfg_regblock ( logic chip6_bound; logic chip7_base; logic chip7_bound; + logic phy_clock_div; } decoded_reg_strb_t; decoded_reg_strb_t decoded_reg_strb; logic decoded_err; @@ -159,6 +160,7 @@ module hyperbus_cfg_regblock ( decoded_reg_strb.chip6_bound = cpuif_req_masked & (cpuif_addr == 7'h6c); decoded_reg_strb.chip7_base = cpuif_req_masked & (cpuif_addr == 7'h70); decoded_reg_strb.chip7_bound = cpuif_req_masked & (cpuif_addr == 7'h74); + decoded_reg_strb.phy_clock_div = cpuif_req_masked & (cpuif_addr == 7'h78); decoded_err = '0; end @@ -355,6 +357,12 @@ module hyperbus_cfg_regblock ( logic load_next; } value; } chip7_bound; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } phy_clock_div; } field_combo_t; field_combo_t field_combo; @@ -510,6 +518,11 @@ module hyperbus_cfg_regblock ( logic [9:0] value; } value; } chip7_bound; + struct { + struct { + logic [7:0] value; + } value; + } phy_clock_div; } field_storage_t; field_storage_t field_storage; @@ -1225,6 +1238,29 @@ module hyperbus_cfg_regblock ( end end assign hwif_out.chip7_bound.value.value = field_storage.chip7_bound.value.value; + // Field: hyperbus_cfg_regs.phy_clock_div.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.phy_clock_div.value.value; + load_next_c = '0; + if(decoded_reg_strb.phy_clock_div && decoded_req_is_wr) begin // SW write + next_c = (field_storage.phy_clock_div.value.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.phy_clock_div.value.next = next_c; + field_combo.phy_clock_div.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.phy_clock_div.value.value <= 8'h8; + end else begin + if(field_combo.phy_clock_div.value.load_next) begin + field_storage.phy_clock_div.value.value <= field_combo.phy_clock_div.value.next; + end + end + end + assign hwif_out.phy_clock_div.value.value = field_storage.phy_clock_div.value.value; //-------------------------------------------------------------------------- // Write response @@ -1332,6 +1368,9 @@ module hyperbus_cfg_regblock ( if(rd_mux_addr == 7'h74) begin readback_data_var[31:22] = field_storage.chip7_bound.value.value; end + if(rd_mux_addr == 7'h78) begin + readback_data_var[7:0] = field_storage.phy_clock_div.value.value; + end readback_data = readback_data_var; readback_done = decoded_req & ~decoded_req_is_wr; readback_err = '0; diff --git a/src/regs/hyperbus_cfg_regblock_pkg.sv b/src/regs/hyperbus_cfg_regblock_pkg.sv index 7d61e63..1208c97 100644 --- a/src/regs/hyperbus_cfg_regblock_pkg.sv +++ b/src/regs/hyperbus_cfg_regblock_pkg.sv @@ -5,7 +5,7 @@ package hyperbus_cfg_regblock_pkg; localparam HYPERBUS_CFG_REGBLOCK_DATA_WIDTH = 32; localparam HYPERBUS_CFG_REGBLOCK_MIN_ADDR_WIDTH = 7; - localparam HYPERBUS_CFG_REGBLOCK_SIZE = 'h78; + localparam HYPERBUS_CFG_REGBLOCK_SIZE = 'h7c; typedef struct { logic hwset; @@ -261,6 +261,14 @@ package hyperbus_cfg_regblock_pkg; hyperbus_cfg_regs__chip7_bound__value__out_t value; } hyperbus_cfg_regs__chip7_bound__out_t; + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__phy_clock_div__value__out_t; + + typedef struct { + hyperbus_cfg_regs__phy_clock_div__value__out_t value; + } hyperbus_cfg_regs__phy_clock_div__out_t; + typedef struct { hyperbus_cfg_regs__t_latency_access__out_t t_latency_access; hyperbus_cfg_regs__en_latency_additional__out_t en_latency_additional; @@ -291,5 +299,6 @@ package hyperbus_cfg_regblock_pkg; hyperbus_cfg_regs__chip6_bound__out_t chip6_bound; hyperbus_cfg_regs__chip7_base__out_t chip7_base; hyperbus_cfg_regs__chip7_bound__out_t chip7_bound; + hyperbus_cfg_regs__phy_clock_div__out_t phy_clock_div; } hyperbus_cfg_regs__out_t; endpackage diff --git a/src/regs/hyperbus_cfg_regs.rdl b/src/regs/hyperbus_cfg_regs.rdl index 7cd9d79..2c2e108 100644 --- a/src/regs/hyperbus_cfg_regs.rdl +++ b/src/regs/hyperbus_cfg_regs.rdl @@ -53,6 +53,16 @@ addrmap hyperbus_cfg_regs { } coarse[7:5]; }; + reg phy_clock_div { + desc = "System-to-PHY clock division factor used by the isochronous top."; + field { + desc = "Must be at least two. Ignored by synchronous and asynchronous tops."; + sw = rw; + hw = r; + reset = 8'd8; + } value[7:0]; + }; + reg address_mask_msb { desc = "Most-significant address bit used by frontend address packing."; field { sw = rw; hw = r; reset = 5'd25; } value[4:0]; @@ -209,4 +219,5 @@ addrmap hyperbus_cfg_regs { chip6_bound chip6_bound @ 0x6c; chip7_base chip7_base @ 0x70; chip7_bound chip7_bound @ 0x74; + phy_clock_div phy_clock_div @ 0x78; }; diff --git a/test/axi_hyper_tb.sv b/test/axi_hyper_tb.sv index b1e7904..c3cf434 100644 --- a/test/axi_hyper_tb.sv +++ b/test/axi_hyper_tb.sv @@ -31,9 +31,9 @@ module axi_hyper_tb parameter time TbTestTime = 4ns, /// DUT variant: 0 is isochronous, 1 is synchronous, 2 is asynchronous parameter int unsigned TbDutVariant = 0, - /// RX delay-line tap value used by variants with explicit delay lines. + /// RX delay-line tap value. parameter int unsigned TbRxDelayLineTaps = 16, - /// TX delay-line tap value used by variants with explicit delay lines. + /// TX delay-line tap value. parameter int unsigned TbTxDelayLineTaps = 19, /// Number of AXI beats in the directed slow read/write stress transactions. parameter int unsigned TbSlowNumBeats = 64, @@ -1015,6 +1015,9 @@ module axi_hyper_tb automatic reg_bus_master_t reg_master = new( reg_bus_mst ); automatic s27ks_cfg0_reg_t s27ks_cfg0 = hyperbus_tb_pkg::s27ks_cfg0_default; + automatic logic [63:0] divider_cycle_snapshot; + automatic logic [63:0] div2_write_cycles; + automatic logic [63:0] div4_write_cycles; // Reset the AXI drivers and scoreboards end_of_sim = 1'b0; @@ -1043,15 +1046,46 @@ module axi_hyper_tb reg_master.send_write(32'h30, 32'h8000_0000, '1, s_reg_error); if (s_reg_error != 1'b0) $error("unexpected error"); - if (TbDutVariant != 0) begin - reg_master.send_write(32'h4 << 2, TbRxDelayLineTaps, '1, s_reg_error); - if (s_reg_error != 1'b0) $error("unexpected error"); - reg_master.send_write(32'h5 << 2, TbTxDelayLineTaps, '1, s_reg_error); + reg_master.send_write(32'h4 << 2, TbRxDelayLineTaps, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_write(32'h5 << 2, TbTxDelayLineTaps, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + + if (TbDutVariant == 0) begin + reg_master.send_read(32'h78, reg_read, s_reg_error); + if ((s_reg_error != 1'b0) || (reg_read != 8)) $error("unexpected divider reset value"); + reg_master.send_write(32'h78, 8'd2, '1, s_reg_error); if (s_reg_error != 1'b0) $error("unexpected error"); end #600350ns; + if (TbDutVariant == 0) begin + // The configuration barrier completes only after the divided clock resumes. + reg_master.send_write(32'h78, 8'd4, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_read(32'h78, reg_read, s_reg_error); + if ((s_reg_error != 1'b0) || (reg_read != 4)) $error("divider update failed"); + + divider_cycle_snapshot = cycle_count; + axi_write_slow(axi_ctrl_mst, 32'h8000_7000, 4, 0); + div4_write_cycles = cycle_count - divider_cycle_snapshot; + + reg_master.send_write(32'h78, 8'd2, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_read(32'h78, reg_read, s_reg_error); + if ((s_reg_error != 1'b0) || (reg_read != 2)) $error("divider restore failed"); + + divider_cycle_snapshot = cycle_count; + axi_write_slow(axi_ctrl_mst, 32'h8000_8000, 4, 0); + div2_write_cycles = cycle_count - divider_cycle_snapshot; + + if (div4_write_cycles <= div2_write_cycles + div2_write_cycles / 2) begin + $error("divider did not reduce PHY throughput: div4=%0d cycles, div2=%0d cycles", + div4_write_cycles, div2_write_cycles); + end + end + run_performance_smoke(axi_ctrl_mst); run_slow_backpressure_test(axi_ctrl_mst, reg_master); check_config_barrier(axi_ctrl_mst, reg_master); @@ -1159,9 +1193,10 @@ endmodule module axi_hyper_tb_isochronous; axi_hyper_tb #( - .TbDutVariant ( 0 ), - .TbCyclTime ( 5ns ), - .TbPhyCyclTime ( 6ns ) + .TbDutVariant ( 0 ), + .TbCyclTime ( 5ns ), + .TbPhyCyclTime ( 6ns ), + .TbTxDelayLineTaps ( 31 ) ) i_axi_hyper_tb (); endmodule diff --git a/test/hyperbus_cfg_regs_tb.sv b/test/hyperbus_cfg_regs_tb.sv index 5c6d4b0..eb2a034 100644 --- a/test/hyperbus_cfg_regs_tb.sv +++ b/test/hyperbus_cfg_regs_tb.sv @@ -116,6 +116,7 @@ module hyperbus_cfg_regs_tb; initial begin : proc_stimulus logic [31:0] status; logic [31:0] chip_addr; + logic [31:0] clock_div; logic [31:0] rx_clk_delay; logic [31:0] tx_clk_delay; @@ -133,6 +134,17 @@ module hyperbus_cfg_regs_tb; $error("Unexpected reset chip address map"); end + reg_read(8'h78, clock_div); + if ((clock_div != 8) || (frontend_cfg.phy_clock_div != 8)) begin + $error("Unexpected PHY clock divider reset value"); + end + + reg_write(8'h78, 8'd4, 1'b0); + reg_read(8'h78, clock_div); + if ((clock_div != 4) || (frontend_cfg.phy_clock_div != 4)) begin + $error("PHY clock divider write did not take effect"); + end + // Exercise both the fine [4:0] and coarse [7:5] delay settings. reg_write(8'h10, 8'hb5, 1'b0); reg_write(8'h14, 8'h6a, 1'b0); From 801397ac21a61689c49fe73b66b8d82ef75fd6f6 Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Thu, 23 Jul 2026 17:48:37 +0200 Subject: [PATCH 10/11] Streamline HyperBus buffering and request flow Remove redundant host and isochronous buffering, adopt common_cells stream and address-decode primitives, and clarify request, atomic, configuration, and adapter control flow. --- Bender.yml | 2 +- include/hyperbus/typedef.svh | 12 +- src/backend/hyperbus_backend.sv | 20 +- src/backend/hyperbus_clock_diff_out.sv | 27 +- src/backend/hyperbus_ddr_out.sv | 18 +- src/backend/hyperbus_delay.sv | 2 +- src/backend/hyperbus_phy.sv | 59 +- src/backend/hyperbus_trx.sv | 48 +- src/hyperbus_async_bridge.sv | 20 + src/hyperbus_asynchronous.sv | 116 ++-- src/hyperbus_atomic_handler.sv | 304 ++++++---- ...perbus_axi.sv => hyperbus_axi_frontend.sv} | 235 ++++---- src/hyperbus_cfg_frontend.sv | 84 ++- src/hyperbus_cfg_regs.sv | 118 ++-- src/hyperbus_iso_bridge.sv | 134 ++--- src/hyperbus_isochronous.sv | 115 ++-- src/hyperbus_midend.sv | 559 +++++++++++------- src/hyperbus_pkg.sv | 27 +- src/hyperbus_read_adapter.sv | 71 ++- src/hyperbus_synchronous.sv | 103 ++-- src/hyperbus_write_adapter.sv | 160 +++-- test/axi_hyper_tb.sv | 12 +- test/dut_if.sv | 15 +- test/fixture_hyperbus.sv | 13 +- test/hyperbus_cfg_regs_tb.sv | 5 +- test/hyperbus_tb.sv | 2 +- test/hyperbus_test_dut.sv | 6 +- 27 files changed, 1359 insertions(+), 928 deletions(-) rename src/{hyperbus_axi.sv => hyperbus_axi_frontend.sv} (60%) diff --git a/Bender.yml b/Bender.yml index c074308..4842667 100644 --- a/Bender.yml +++ b/Bender.yml @@ -55,7 +55,7 @@ sources: - src/regs/hyperbus_cfg_regblock.sv - src/hyperbus_cfg_regs.sv - src/backend/hyperbus_phy.sv - - src/hyperbus_axi.sv + - src/hyperbus_axi_frontend.sv - src/hyperbus_atomic_handler.sv - src/hyperbus_midend.sv - src/hyperbus_cfg_frontend.sv diff --git a/include/hyperbus/typedef.svh b/include/hyperbus/typedef.svh index 9474b9f..cd20bbb 100644 --- a/include/hyperbus/typedef.svh +++ b/include/hyperbus/typedef.svh @@ -5,6 +5,10 @@ `ifndef HYPERBUS_TYPEDEF_SVH_ `define HYPERBUS_TYPEDEF_SVH_ +//////////////////////////////// +// Protocol-neutral host link // +//////////////////////////////// + `define HYPERBUS_TYPEDEF_HOST_CMD_T(__name, __addr_t) \ typedef struct packed { \ logic write; \ @@ -68,7 +72,11 @@ `HYPERBUS_TYPEDEF_HOST_RSP_T(__name``_rsp_t, __name``_r_t, \ __name``_wrsp_t) -`define HYPERBUS_TYPEDEF_LINK_ALL_CT(__name, __num_phys, __num_chips) \ +//////////////////////////// +// Midend-to-backend link // +//////////////////////////// + +`define HYPERBUS_TYPEDEF_LINK_ALL_CT(__name, __num_phys) \ typedef struct packed { \ logic [(16*__num_phys)-1:0] data; \ logic last; \ @@ -84,7 +92,7 @@ } __name``_wrsp_t; \ typedef struct packed { \ hyperbus_pkg::hyper_tf_t trans; \ - logic [__num_chips-1:0] cs; \ + logic [hyperbus_pkg::HyperNumChips-1:0] cs; \ } __name``_cmd_t; \ typedef struct packed { \ __name``_cmd_t cmd; \ diff --git a/src/backend/hyperbus_backend.sv b/src/backend/hyperbus_backend.sv index f7349c8..ac3e4d2 100644 --- a/src/backend/hyperbus_backend.sv +++ b/src/backend/hyperbus_backend.sv @@ -6,7 +6,6 @@ `include "common_cells/registers.svh" module hyperbus_backend #( - parameter int unsigned NumChips = -1, parameter int unsigned NumPhys = 2, parameter int unsigned StartupCycles = 60000, parameter int unsigned SyncStages = 2, @@ -31,7 +30,7 @@ module hyperbus_backend #( input hyper_req_t req_i, output hyper_rsp_t rsp_o, - output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, + output logic [NumPhys-1:0][hyperbus_pkg::HyperNumChips-1:0] hyper_cs_no, output logic [NumPhys-1:0] hyper_ck_o, output logic [NumPhys-1:0] hyper_ck_no, output logic [NumPhys-1:0] hyper_rwds_o, @@ -43,12 +42,15 @@ module hyperbus_backend #( output logic [NumPhys-1:0] hyper_reset_no ); + ///////////////////////////// + // Configuration and clock // + ///////////////////////////// + hyperbus_pkg::phy_cfg_t cfg_q; - logic cfg_apply_accepted; - logic phy_busy_any; - logic clk_tx; + logic cfg_apply_accepted; + logic phy_busy_any; + logic clk_tx; - `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= 8) `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) `ASSERT_INIT(SyncStagesValid, SyncStages >= 2) @@ -69,6 +71,10 @@ module hyperbus_backend #( .out_o ( clk_tx ) ); + ///////////////////// + // Physical lanes // + ///////////////////// + if (NumPhys == 2) begin : gen_dual_phy hyperbus_pkg::phy_rx_t [NumPhys-1:0] phy_rx; hyperbus_pkg::phy_rx_t [NumPhys-1:0] buffered_rx; @@ -169,7 +175,6 @@ module hyperbus_backend #( ); hyperbus_phy #( - .NumChips ( NumChips ), .StartupCycles ( StartupCycles ), .NumPhys ( NumPhys ), .SyncStages ( SyncStages ) @@ -211,7 +216,6 @@ module hyperbus_backend #( end end else begin : gen_single_phy hyperbus_phy #( - .NumChips ( NumChips ), .StartupCycles ( StartupCycles ), .NumPhys ( NumPhys ), .SyncStages ( SyncStages ) diff --git a/src/backend/hyperbus_clock_diff_out.sv b/src/backend/hyperbus_clock_diff_out.sv index 67c6ae6..72ce9fc 100644 --- a/src/backend/hyperbus_clock_diff_out.sv +++ b/src/backend/hyperbus_clock_diff_out.sv @@ -9,28 +9,27 @@ (* no_ungroup *) (* no_boundary_optimization *) (* keep_hierarchy = "yes" *) -module hyperbus_clock_diff_out -( +module hyperbus_clock_diff_out ( input logic in_i, - input logic en_i, //high enable + input logic en_i, output logic out_o, output logic out_no ); - `ifdef FPGA_EMUL +`ifdef FPGA_EMUL - logic en_sync; + logic en_sync; - always_latch - begin - if (in_i == 1'b0) - en_sync <= en_i; - end + always_latch begin + if (in_i == 1'b0) begin + en_sync <= en_i; + end + end - assign out_o = in_i & en_sync; - assign out_no = ~out_o; + assign out_o = in_i & en_sync; + assign out_no = ~out_o; - `else +`else tc_clk_gating i_hyper_ck_gating ( .clk_i ( in_i ), @@ -44,6 +43,6 @@ module hyperbus_clock_diff_out .clk_o ( out_no ) ); - `endif // !`ifdef FPGA_EMUL +`endif endmodule diff --git a/src/backend/hyperbus_ddr_out.sv b/src/backend/hyperbus_ddr_out.sv index d737d15..38f57f5 100644 --- a/src/backend/hyperbus_ddr_out.sv +++ b/src/backend/hyperbus_ddr_out.sv @@ -7,7 +7,7 @@ module hyperbus_ddr_out #( parameter logic Init = 1'b0 -)( +) ( input logic clk_i, input logic rst_ni, input logic d0_i, @@ -18,13 +18,13 @@ module hyperbus_ddr_out #( logic q1; `ifdef FPGA_EMUL - always_comb - begin - if(clk_i == 1'b0) - q_o = q1; - else - q_o = q0; - end + always_comb begin + if (clk_i == 1'b0) begin + q_o = q1; + end else begin + q_o = q0; + end + end `else tc_clk_mux2 i_ddrmux ( .clk_o ( q_o ), @@ -32,7 +32,7 @@ module hyperbus_ddr_out #( .clk1_i ( q0 ), .clk_sel_i ( clk_i ) ); -`endif // !`ifdef FPGA_EMUL +`endif always_ff @(posedge clk_i or negedge rst_ni) begin if (~rst_ni) begin diff --git a/src/backend/hyperbus_delay.sv b/src/backend/hyperbus_delay.sv index 00ea849..43dffc8 100644 --- a/src/backend/hyperbus_delay.sv +++ b/src/backend/hyperbus_delay.sv @@ -20,7 +20,7 @@ module hyperbus_delay ( // Additional delay can be added for debug purposes, // the upper 3 bits are reserved for this optional additional delay configurable_delay #( - .NUM_STEPS(32) + .NUM_STEPS ( 32 ) ) i_delay ( .clk_i ( in_i ), .delay_i ( delay_i[4:0] ), diff --git a/src/backend/hyperbus_phy.sv b/src/backend/hyperbus_phy.sv index c531e67..031a830 100644 --- a/src/backend/hyperbus_phy.sv +++ b/src/backend/hyperbus_phy.sv @@ -10,12 +10,12 @@ `include "common_cells/assertions.svh" module hyperbus_phy import hyperbus_pkg::*; #( - parameter int unsigned NumChips = 2, parameter int unsigned NumPhys = -1, parameter int unsigned TimerWidth = 16, parameter int unsigned RxFifoLogDepth = 3, parameter int unsigned SyncStages = 2, - parameter int unsigned StartupCycles = 300 /*us*/ * 200 /*MHz*/ // Conservative maximum frequency estimate + // Conservative startup delay: 300 us at 200 MHz. + parameter int unsigned StartupCycles = 300 * 200 )( input logic clk_i, input logic clk_tx_i, @@ -29,7 +29,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( input logic trans_valid_i, output logic trans_ready_o, input hyper_tf_t trans_i, // TODO: increase burst width! - input logic [NumChips-1:0] trans_cs_i, + input logic [HyperNumChips-1:0] trans_cs_i, // Transmitting channel input logic tx_valid_i, output logic tx_ready_o, @@ -47,7 +47,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( input logic b_ready_i, output logic b_error_o, // Physical interface - output logic [NumChips-1:0] hyper_cs_no, + output logic [HyperNumChips-1:0] hyper_cs_no, output logic hyper_ck_o, output logic hyper_ck_no, output logic hyper_rwds_o, @@ -71,11 +71,14 @@ module hyperbus_phy import hyperbus_pkg::*; #( assign words_per_beat = (NumPhys == 2 && cfg_i.dual_phy) ? 2 : 1; - // PHY state + ////////////////////// + // Persistent state // + ////////////////////// + hyper_phy_state_t state_d, state_q; logic [TimerWidth-1:0] timer_d, timer_q; hyper_tf_t tf_d, tf_q; - logic [NumChips-1:0] cs_d, cs_q; + logic [HyperNumChips-1:0] cs_d, cs_q; logic add_latency_d, add_latency_q; // Whether B response is pending @@ -88,7 +91,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( logic r_outstand_inc; logic r_outstand_dec; - // Auxiliar control signals + // Auxiliary control signals logic ctl_write_zero_lat; logic ctl_add_latency; logic ctl_tf_burst_last; @@ -105,7 +108,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( // Command-address hyper_phy_ca_t ca; - // Transciever IO + // Transceiver I/O logic trx_clk_ena; logic trx_cs_ena; logic trx_rwds_sample; @@ -120,12 +123,11 @@ module hyperbus_phy import hyperbus_pkg::*; #( logic trx_rx_valid; logic trx_rx_ready; - // ================= - // Transciever - // ================= + ////////////////////// + // Transceiver I/O // + ////////////////////// hyperbus_trx #( - .NumChips ( NumChips ), .RxFifoLogDepth ( RxFifoLogDepth ), .SyncStages ( SyncStages ) ) i_trx ( @@ -160,9 +162,9 @@ module hyperbus_phy import hyperbus_pkg::*; #( .hyper_reset_no ); - // ============== - // Dataflow - // ============== + ////////////// + // Dataflow // + ////////////// // Command-address assign ca = hyper_phy_ca_t '{ @@ -226,9 +228,9 @@ module hyperbus_phy import hyperbus_pkg::*; #( else if (r_outstand_dec & ~r_outstand_inc) r_outstand_q <= r_outstand_q - 1; end - // ============= - // Control - // ============= + ///////////// + // Control // + ///////////// // Auxiliary control signals assign ctl_write_zero_lat = tf_q.address_space & tf_q.write; @@ -265,7 +267,8 @@ module hyperbus_phy import hyperbus_pkg::*; #( trx_tx_rwds_oe = 1'b0; trx_tx_data_oe = 1'b0; // State-dependent logic - case (state_q) + unique case (state_q) + // Hold chip select inactive until the startup delay expires. Startup: begin trx_cs_ena = 1'b0; // Timer resets to parameterized startup delay @@ -273,6 +276,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( state_d = Idle; end end + // Accept a transfer only after all responses from the previous one have drained. Idle: begin trx_cs_ena = 1'b0; timer_d = timer_q; @@ -302,6 +306,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( trx_tx_data_oe = 1'b1; end end + // Assert chip select early when RWDS needs extra setup time before CK starts. DelayCK: begin trx_clk_ena = 1'b0; trx_rwds_sample_ena = ~ctl_write_zero_lat; @@ -310,6 +315,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( state_d = SendCA; end end + // Shift the three command/address words onto DQ. SendCA: begin // Dataflow handled outside FSM trx_clk_ena = 1'b1; @@ -326,6 +332,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( end end end + // Wait one access-latency interval and sample the additional-latency request. WaitLatAccess: begin trx_clk_ena = 1'b1; trx_tx_data_oe = 1'b1; @@ -361,6 +368,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( add_latency_d = 1'b0; end end + // Complete the requested second access-latency interval. WaitAddLatAccess: begin // Same as WaitLatAccess but without possibility // of adding another latency count @@ -379,6 +387,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( end end end + // Capture read data until this segment completes or reaches its time limit. Read: begin // Dataflow handled outside FSM trx_rx_clk_set = 1'b1; @@ -398,6 +407,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( state_d = WaitXfer; end end + // Transmit write data until this segment completes or reaches its time limit. Write: begin // Drive DQ lines in write mode trx_tx_data_oe = 1'b1; @@ -420,6 +430,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( state_d = WaitXfer; end end + // Keep chip select asserted until the final generated clock edge has settled. WaitXfer: begin // Wait for FFed Clock and output to stop // May have to be prolonged for potential future devices with t_CSH > 0 @@ -428,6 +439,7 @@ module hyperbus_phy import hyperbus_pkg::*; #( state_d = WaitRWR; end end + // Enforce read/write recovery, then continue a split transfer or return idle. WaitRWR: begin trx_cs_ena = 1'b0; if (ctl_timer_rwr_done) begin @@ -445,6 +457,15 @@ module hyperbus_phy import hyperbus_pkg::*; #( end end end + // Recover safely from an invalid state through the normal startup sequence. + default: begin + state_d = Startup; + timer_d = StartupCycles; + tf_d = hyper_tf_t'{burst_type: 1'b1, default: '0}; + cs_d = '0; + add_latency_d = 1'b0; + trx_cs_ena = 1'b0; + end endcase end diff --git a/src/backend/hyperbus_trx.sv b/src/backend/hyperbus_trx.sv index 73fc128..d0b882e 100644 --- a/src/backend/hyperbus_trx.sv +++ b/src/backend/hyperbus_trx.sv @@ -9,7 +9,6 @@ `include "common_cells/assertions.svh" module hyperbus_trx #( - parameter int unsigned NumChips = 2, parameter int unsigned RxFifoLogDepth = 3, parameter int unsigned SyncStages = 2 )( @@ -20,7 +19,7 @@ module hyperbus_trx #( input logic test_mode_i, // Transceiver control: facing controller - input logic [NumChips-1:0] cs_i, + input logic [hyperbus_pkg::HyperNumChips-1:0] cs_i, input logic cs_ena_i, output logic rwds_sample_o, input logic rwds_sample_ena_i, @@ -38,7 +37,7 @@ module hyperbus_trx #( output logic rx_valid_o, input logic rx_ready_i, // Physical interface: facing HyperBus - output logic [NumChips-1:0] hyper_cs_no, + output logic [hyperbus_pkg::HyperNumChips-1:0] hyper_cs_no, output logic hyper_ck_o, output logic hyper_ck_no, output logic hyper_rwds_o, @@ -50,27 +49,14 @@ module hyperbus_trx #( output logic hyper_reset_no ); - logic tx_clk_ena_q; - logic rx_rwds_90; - - // Delayed clock enable synchronous with data - - // Intermediate RX signals for RWDS domain - logic rx_rwds_clk_ena; - logic rx_rwds_clk_orig; - logic rx_rwds_clk; - logic rx_rwds_clk_n; - logic rx_capture_rst; - logic [15:0] rx_rwds_fifo_in; - logic rx_rwds_fifo_valid; - logic rx_rwds_fifo_ready; - // Feed through async reset assign hyper_reset_no = rst_ni; - // ================= - // TX + control - // ================= + ////////////////// + // TX and control // + ////////////////// + + logic tx_clk_ena_q; // The delayed differential output clock samples output bytes centrally. // TODO: tx_clk_ena_q must arrive before clk_tx_i to avoid disturbing clock gating. @@ -125,9 +111,19 @@ module hyperbus_trx #( end end - // ======== - // RX - // ======== + ///////////////////// + // RX data capture // + ///////////////////// + + logic rx_rwds_90; + logic rx_rwds_clk_ena; + logic rx_rwds_clk_orig; + logic rx_rwds_clk; + logic rx_rwds_clk_n; + logic rx_capture_rst; + logic [15:0] rx_rwds_fifo_in; + logic rx_rwds_fifo_valid; + logic rx_rwds_fifo_ready; // Sample RWDS for extra latency determination. always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_rwds_sample @@ -169,8 +165,8 @@ module hyperbus_trx #( .clk_o ( rx_rwds_clk_orig ) ); - // Reset RX state on async reset or on gated clock (whenever inactive) - // TODO: is this safe? Replace with tech cells? + // Reset RX state on async reset or on gated clock (whenever inactive) + // TODO: is this safe? Replace with tech cells? assign rx_capture_rst = !rst_ni || (!rx_rwds_clk_ena && !test_mode_i); // RX data is valid one cycle after each RX soft reset diff --git a/src/hyperbus_async_bridge.sv b/src/hyperbus_async_bridge.sv index 9d97ed2..e2644c2 100644 --- a/src/hyperbus_async_bridge.sv +++ b/src/hyperbus_async_bridge.sv @@ -36,6 +36,10 @@ module hyperbus_async_bridge #( input logic cfg_apply_ready_i ); + //////////////////////////// + // Configuration crossing // + //////////////////////////// + logic cfg_apply_accepted; logic cfg_apply_pending_d, cfg_apply_pending_q; @@ -81,6 +85,10 @@ module hyperbus_async_bridge #( .dst_ready_i ( cfg_apply_ready_i ) ); + ////////////////////// + // Command crossing // + ////////////////////// + cdc_2phase_clearable #( .T ( hyper_cmd_t ), .SYNC_STAGES ( CdcSyncStages ) @@ -101,6 +109,10 @@ module hyperbus_async_bridge #( .dst_ready_i ( backend_rsp_i.cmd_ready ) ); + ///////////////////////////// + // Write-response crossing // + ///////////////////////////// + cdc_2phase_clearable #( .T ( hyper_wrsp_t ), .SYNC_STAGES ( CdcSyncStages ) @@ -121,6 +133,10 @@ module hyperbus_async_bridge #( .dst_ready_i ( frontend_req_i.wrsp_ready ) ); + /////////////////////// + // TX data crossing // + /////////////////////// + cdc_fifo_gray_clearable #( .T ( hyper_tx_t ), .LOG_DEPTH ( TxFifoLogDepth ), @@ -142,6 +158,10 @@ module hyperbus_async_bridge #( .dst_ready_i ( backend_rsp_i.tx_ready ) ); + /////////////////////// + // RX data crossing // + /////////////////////// + cdc_fifo_gray_clearable #( .T ( hyper_rx_t ), .LOG_DEPTH ( RxFifoLogDepth ), diff --git a/src/hyperbus_asynchronous.sv b/src/hyperbus_asynchronous.sv index 376890a..3069b4e 100644 --- a/src/hyperbus_asynchronous.sv +++ b/src/hyperbus_asynchronous.sv @@ -6,23 +6,24 @@ `include "common_cells/assertions.svh" module hyperbus_asynchronous #( - parameter int unsigned NumChips = -1, - parameter int unsigned NumPhys = 2, - parameter int unsigned AxiAddrWidth = -1, - parameter int unsigned AxiDataWidth = -1, - parameter int unsigned AxiIdWidth = -1, - parameter int unsigned AxiUserWidth = -1, - parameter type axi_req_t = logic, - parameter type axi_rsp_t = logic, - parameter int unsigned RegDataWidth = -1, - parameter type reg_req_t = logic, - parameter type reg_rsp_t = logic, - parameter type axi_rule_t = logic, - parameter int unsigned RxFifoLogDepth = 3, - parameter int unsigned TxFifoLogDepth = 3, - parameter int unsigned PhyStartupCycles = 300 * 200, - parameter int unsigned SyncStages = 2, - parameter int unsigned CdcSyncStages = 3 + parameter int unsigned NumPhys = 2, + parameter int unsigned AxiAddrWidth = -1, + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned AxiIdWidth = -1, + parameter int unsigned AxiUserWidth = -1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter int unsigned RegDataWidth = -1, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type axi_rule_t = logic, + parameter int unsigned HostCommandDepth = 8, + parameter int unsigned HostWriteBufferBytes = 128, + parameter int unsigned RxFifoLogDepth = 3, + parameter int unsigned TxFifoLogDepth = 3, + parameter int unsigned PhyStartupCycles = 300 * 200, + parameter int unsigned SyncStages = 2, + parameter int unsigned CdcSyncStages = 3 ) ( input logic clk_sys_i, input logic rst_sys_ni, @@ -39,7 +40,7 @@ module hyperbus_asynchronous #( input reg_req_t reg_req_i, output reg_rsp_t reg_rsp_o, - output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, + output logic [NumPhys-1:0][hyperbus_pkg::HyperNumChips-1:0] hyper_cs_no, output logic [NumPhys-1:0] hyper_ck_o, output logic [NumPhys-1:0] hyper_ck_no, output logic [NumPhys-1:0] hyper_rwds_o, @@ -57,10 +58,20 @@ module hyperbus_asynchronous #( typedef logic [AxiDataWidth-1:0] host_data_t; typedef logic [AxiDataWidth/8-1:0] host_strb_t; `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) - `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys, NumChips) + `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys) + + ///////////////////// + // Clock and reset // + ///////////////////// + + logic clk_backend; + logic rst_backend_n; + logic rst_phy_async_n; + + //////////////////////// + // Configuration path // + //////////////////////// - logic clk_backend; - logic rst_backend_n; hyperbus_pkg::phy_cfg_t frontend_cfg_apply; logic frontend_cfg_apply_valid; logic frontend_cfg_apply_ready; @@ -73,7 +84,11 @@ module hyperbus_asynchronous #( logic backend_cfg_apply_ready; hyperbus_pkg::frontend_cfg_t frontend_cfg; - axi_rule_t [NumChips-1:0] frontend_chip_rules; + axi_rule_t [hyperbus_pkg::HyperNumChips-1:0] frontend_chip_rules; + + //////////////////// + // Dataflow links // + //////////////////// host_req_t host_req; host_rsp_t host_rsp; @@ -84,8 +99,6 @@ module hyperbus_asynchronous #( hyper_req_t backend_req; hyper_rsp_t backend_rsp; - logic rst_phy_async_n; - // Clearable CDCs permit an independent PHY reset without creating phantom // transfers. Such a reset can discard a pending transfer and is therefore // only a supported software operation while the controller is idle. @@ -101,8 +114,11 @@ module hyperbus_asynchronous #( .init_no ( ) ); + //////////////////////////// + // Configuration frontend // + //////////////////////////// + hyperbus_cfg_frontend #( - .NumChips ( NumChips ), .NumPhys ( NumPhys ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), @@ -129,6 +145,10 @@ module hyperbus_asynchronous #( .decode_error_i ( midend_decode_error ) ); + ////////////////// + // AXI frontend // + ////////////////// + hyperbus_axi_frontend #( .AxiDataWidth ( AxiDataWidth ), .AxiAddrWidth ( AxiAddrWidth ), @@ -149,23 +169,28 @@ module hyperbus_asynchronous #( .host_rsp_i ( host_rsp ) ); + //////////// + // Midend // + //////////// + hyperbus_midend #( - .HostAddrWidth ( AxiAddrWidth ), - .HostDataWidth ( AxiDataWidth ), - .NumChips ( NumChips ), - .NumPhys ( NumPhys ), - .host_cmd_t ( host_cmd_t ), - .host_w_t ( host_w_t ), - .host_r_t ( host_r_t ), - .host_wrsp_t ( host_wrsp_t ), - .host_req_t ( host_req_t ), - .host_rsp_t ( host_rsp_t ), - .hyper_rx_t ( hyper_rx_t ), - .hyper_tx_t ( hyper_tx_t ), - .hyper_cmd_t ( hyper_cmd_t ), - .hyper_req_t ( hyper_req_t ), - .hyper_rsp_t ( hyper_rsp_t ), - .rule_t ( axi_rule_t ) + .HostAddrWidth ( AxiAddrWidth ), + .HostDataWidth ( AxiDataWidth ), + .NumPhys ( NumPhys ), + .HostCommandDepth ( HostCommandDepth ), + .HostWriteBufferBytes ( HostWriteBufferBytes ), + .host_cmd_t ( host_cmd_t ), + .host_w_t ( host_w_t ), + .host_r_t ( host_r_t ), + .host_wrsp_t ( host_wrsp_t ), + .host_req_t ( host_req_t ), + .host_rsp_t ( host_rsp_t ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .hyper_cmd_t ( hyper_cmd_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ), + .rule_t ( axi_rule_t ) ) i_midend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), @@ -179,6 +204,10 @@ module hyperbus_asynchronous #( .hyper_link_rsp_i ( midend_rsp ) ); + ///////////////////////// + // Asynchronous bridge // + ///////////////////////// + hyperbus_async_bridge #( .RxFifoLogDepth ( RxFifoLogDepth ), .TxFifoLogDepth ( TxFifoLogDepth ), @@ -207,8 +236,11 @@ module hyperbus_asynchronous #( .cfg_apply_ready_i ( backend_cfg_apply_ready ) ); + ///////////// + // Backend // + ///////////// + hyperbus_backend #( - .NumChips ( NumChips ), .NumPhys ( NumPhys ), .StartupCycles ( PhyStartupCycles ), .SyncStages ( SyncStages ), diff --git a/src/hyperbus_atomic_handler.sv b/src/hyperbus_atomic_handler.sv index 4daddea..b301199 100644 --- a/src/hyperbus_atomic_handler.sv +++ b/src/hyperbus_atomic_handler.sv @@ -4,6 +4,7 @@ `include "common_cells/registers.svh" +/// Execute one atomic request as an exclusive backend read-modify-write sequence. module hyperbus_atomic_handler #( parameter int unsigned HostAddrWidth = -1, parameter int unsigned HostDataWidth = -1, @@ -70,6 +71,9 @@ module hyperbus_atomic_handler #( logic atomic_return; } atomic_request_t; + ////////////////////// + // Persistent state // + ////////////////////// atomic_state_e state_d, state_q; atomic_request_t request_d, request_q; host_data_t operand_d, operand_q; @@ -78,119 +82,173 @@ module hyperbus_atomic_handler #( logic read_pending_d, read_pending_q; logic write_pending_d, write_pending_q; - host_data_t value_mask; - host_data_t old_value; - host_data_t operand_value; - host_data_t swap_value; - host_data_t result_value; - host_data_t write_data; - int unsigned byte_offset; - int unsigned operand_bytes; - int unsigned operand_bits; + //////////////////////// + // Request sequencing // + //////////////////////// + atomic_request_t request_in; + logic operation_valid; + hyperbus_pkg::hyper_resp_e initial_response, backend_write_response; + logic atomic_started, operand_accepted; + logic valid_operand_accepted, invalid_operand_completed; + logic command_accepted; + logic backend_read_accepted, backend_read_succeeded, backend_read_failed; + logic backend_write_accepted, backend_write_rsp_accepted; + logic host_read_accepted, host_write_rsp_accepted; + + assign request_in = '{ + addr: request_i.addr, + size: request_i.size, + atomic_op: request_i.atomic_op, + atomic_return: request_i.atomic_return + }; + + // A start captures the request even when validation failed. In that case the + // handler only drains its write data and returns an atomic error. + assign atomic_started = (state_q == Idle) && start_i; + assign operation_valid = response_q == hyperbus_pkg::HyperRespOkay; + assign initial_response = request_valid_i ? hyperbus_pkg::HyperRespOkay : + hyperbus_pkg::HyperRespAtomicError; + assign operand_accepted = host_w_valid_i && host_w_ready_o; + assign valid_operand_accepted = operand_accepted && operation_valid; + assign invalid_operand_completed = operand_accepted && !operation_valid && host_w_i.last; + + // These events advance the backend read-modify-write sequence. + // Read errors skip the write, write errors are reported after the write response. + assign command_accepted = command_valid_o && command_ready_i; + assign backend_read_accepted = read_valid_i && read_ready_o; + assign backend_read_succeeded = backend_read_accepted && + (read_i.resp == hyperbus_pkg::HyperRespOkay); + assign backend_read_failed = backend_read_accepted && + (read_i.resp != hyperbus_pkg::HyperRespOkay); + assign backend_write_accepted = write_valid_o && write_ready_i; + assign backend_write_rsp_accepted = write_rsp_valid_i && write_rsp_ready_o; + assign backend_write_response = write_rsp_error_i ? + hyperbus_pkg::HyperRespAccessError : hyperbus_pkg::HyperRespOkay; + + // Hold both host responses until independently accepted. + // Completion is asserted in the cycle where the final pending response is accepted. + assign host_read_accepted = host_r_valid_o && host_r_ready_i; + assign host_write_rsp_accepted = host_wrsp_valid_o && host_wrsp_ready_i; + assign completed_o = (state_q == ReturnResponse) && + (!read_pending_q || host_read_accepted) && + (!write_pending_q || host_write_rsp_accepted); assign active_o = state_q != Idle; assign command_valid_o = (state_q == ReadCommand) || (state_q == WriteCommand); - assign completed_o = (state_q == ReturnResponse) && - (!read_pending_q || (host_r_valid_o && host_r_ready_i)) && - (!write_pending_q || (host_wrsp_valid_o && host_wrsp_ready_i)); + ////////////////////// + // Backend commands // + ////////////////////// + // Atomic commands are converted into one backend read followed by one write. always_comb begin : proc_command command_o = '0; command_o.write = state_q == WriteCommand; command_o.addr = request_q.addr; command_o.beats = 1; - command_o.size = request_q.size; + command_o.size = ((request_q.atomic_op == hyperbus_pkg::HyperAtomicCompare) && + (request_q.size != '0)) ? + request_q.size - 1'b1 : request_q.size; command_o.burst = hyperbus_pkg::HyperBurstIncr; command_o.atomic_op = hyperbus_pkg::HyperAtomicNone; command_o.atomic_return = 1'b0; command_o.ordered = 1'b1; - if ((request_q.atomic_op == hyperbus_pkg::HyperAtomicCompare) && - (request_q.size != '0)) begin - command_o.size = request_q.size - 1'b1; - end end - always_comb begin : proc_alu - byte_offset = 0; + //////////////// + // Atomic ALU // + //////////////// + // The operation works on one contiguous byte-lane range within the host data word. + host_data_t value_mask, write_mask; + host_data_t alu_operand_a, alu_operand_b; + host_data_t compare_value, swap_value; + host_data_t alu_result, shifted_alu_result; + host_data_t preserved_data, write_data; + int unsigned byte_offset, operand_bytes, operand_bits; + logic alu_enable, alu_equal; + logic alu_signed_less, alu_unsigned_less; + + // Select and align the memory value and write operand before the ALU. + always_comb begin : proc_alu_operands + byte_offset = 0; operand_bytes = 1; - operand_bits = 8; - value_mask = '1; - old_value = read_data_q; - operand_value = operand_q; - swap_value = operand_q >> 8; - result_value = old_value; - write_data = read_data_q; + operand_bits = 8; + value_mask = '1; if (response_q == hyperbus_pkg::HyperRespOkay) begin byte_offset = unsigned'(request_q.addr[HostBusAddrWidth-1:0]); operand_bytes = 1 << ((request_q.atomic_op == hyperbus_pkg::HyperAtomicCompare) ? - (request_q.size - 1'b1) : request_q.size); - operand_bits = operand_bytes * 8; - if (operand_bits < HostDataWidth) begin - value_mask = value_mask >> (HostDataWidth - operand_bits); - end + request_q.size - 1'b1 : request_q.size); + operand_bits = operand_bytes * 8; + value_mask = value_mask >> (HostDataWidth - operand_bits); + end - old_value = read_data_q >> (byte_offset * 8); - operand_value = operand_q >> (byte_offset * 8); - swap_value = operand_value >> operand_bits; - result_value = old_value; + alu_operand_a = (read_data_q >> (byte_offset * 8)) & value_mask; + // Compare operations carry the compare value below the replacement value. + compare_value = (operand_q >> (byte_offset * 8)) & value_mask; + swap_value = (operand_q >> ((byte_offset * 8) + operand_bits)) & value_mask; + alu_operand_b = (request_q.atomic_op == hyperbus_pkg::HyperAtomicCompare) ? + swap_value : compare_value; + end + + assign alu_enable = (state_q == WriteData) && + (response_q == hyperbus_pkg::HyperRespOkay); + assign alu_equal = alu_operand_a == compare_value; + assign alu_unsigned_less = alu_operand_a < alu_operand_b; + assign alu_signed_less = (alu_operand_a[operand_bits-1] != + alu_operand_b[operand_bits-1]) ? + alu_operand_a[operand_bits-1] : alu_unsigned_less; + // Apply the selected operation to the aligned operands. + always_comb begin : proc_alu + alu_result = alu_operand_a; + if (alu_enable) begin unique case (request_q.atomic_op) - hyperbus_pkg::HyperAtomicSwap: result_value = operand_value; - hyperbus_pkg::HyperAtomicCompare: begin - if ((old_value & value_mask) == (operand_value & value_mask)) begin - result_value = swap_value; - end - end - hyperbus_pkg::HyperAtomicAdd: result_value = old_value + operand_value; - hyperbus_pkg::HyperAtomicClear: result_value = old_value & ~operand_value; - hyperbus_pkg::HyperAtomicXor: result_value = old_value ^ operand_value; - hyperbus_pkg::HyperAtomicSet: result_value = old_value | operand_value; - hyperbus_pkg::HyperAtomicSignedMax: begin - if ((old_value[operand_bits-1] && !operand_value[operand_bits-1]) || - ((old_value[operand_bits-1] == operand_value[operand_bits-1]) && - ((old_value & value_mask) < (operand_value & value_mask)))) begin - result_value = operand_value; - end - end - hyperbus_pkg::HyperAtomicSignedMin: begin - if ((!old_value[operand_bits-1] && operand_value[operand_bits-1]) || - ((old_value[operand_bits-1] == operand_value[operand_bits-1]) && - ((old_value & value_mask) > (operand_value & value_mask)))) begin - result_value = operand_value; - end - end - hyperbus_pkg::HyperAtomicUnsignedMax: begin - if ((old_value & value_mask) < (operand_value & value_mask)) begin - result_value = operand_value; - end - end - hyperbus_pkg::HyperAtomicUnsignedMin: begin - if ((old_value & value_mask) > (operand_value & value_mask)) begin - result_value = operand_value; - end - end + hyperbus_pkg::HyperAtomicSwap: alu_result = alu_operand_b; + hyperbus_pkg::HyperAtomicCompare: alu_result = alu_equal ? + alu_operand_b : alu_operand_a; + hyperbus_pkg::HyperAtomicAdd: alu_result = alu_operand_a + alu_operand_b; + hyperbus_pkg::HyperAtomicAnd: alu_result = alu_operand_a & alu_operand_b; + hyperbus_pkg::HyperAtomicClear: alu_result = alu_operand_a & ~alu_operand_b; + hyperbus_pkg::HyperAtomicXor: alu_result = alu_operand_a ^ alu_operand_b; + hyperbus_pkg::HyperAtomicSet: alu_result = alu_operand_a | alu_operand_b; + hyperbus_pkg::HyperAtomicSignedMax: alu_result = alu_signed_less ? + alu_operand_b : alu_operand_a; + hyperbus_pkg::HyperAtomicSignedMin: alu_result = alu_signed_less ? + alu_operand_a : alu_operand_b; + hyperbus_pkg::HyperAtomicUnsignedMax: alu_result = alu_unsigned_less ? + alu_operand_b : alu_operand_a; + hyperbus_pkg::HyperAtomicUnsignedMin: alu_result = alu_unsigned_less ? + alu_operand_a : alu_operand_b; default:; endcase - - write_data = read_data_q & ~(value_mask << (byte_offset * 8)); - write_data |= (result_value & value_mask) << (byte_offset * 8); end end + assign write_mask = value_mask << (byte_offset * 8); + assign preserved_data = read_data_q & ~write_mask; + assign shifted_alu_result = (alu_result & value_mask) << (byte_offset * 8); + assign write_data = alu_enable ? preserved_data | shifted_alu_result : read_data_q; + + //////////////////// + // Stream outputs // + //////////////////// always_comb begin : proc_outputs host_w_ready_o = state_q == WaitWriteData; + host_r_o = '0; host_r_o.data = read_data_q; host_r_o.resp = response_q; host_r_o.last = 1'b1; host_r_o.atomic_ok = response_q == hyperbus_pkg::HyperRespOkay; host_r_valid_o = (state_q == ReturnResponse) && read_pending_q; + host_wrsp_o = '0; host_wrsp_o.resp = response_q; host_wrsp_o.atomic_ok = response_q == hyperbus_pkg::HyperRespOkay; host_wrsp_valid_o = (state_q == ReturnResponse) && write_pending_q; + read_ready_o = state_q == ReadData; + write_o = '0; write_o.data = write_data; write_o.last = 1'b1; @@ -198,9 +256,15 @@ module hyperbus_atomic_handler #( write_o.strb[i] = (i >= byte_offset) && (i < byte_offset + operand_bytes); end write_valid_o = state_q == WriteData; + write_rsp_ready_o = state_q == WriteResponse; end + /////////////////////////// + // Control state machine // + /////////////////////////// + // A valid request consumes one operand, reads the old value, writes the ALU result, and + // returns both responses. Invalid requests drain their operands; backend errors return early. always_comb begin : proc_state state_d = state_q; request_d = request_q; @@ -211,63 +275,89 @@ module hyperbus_atomic_handler #( write_pending_d = write_pending_q; unique case (state_q) + // Capture one validated or rejected atomic request. Idle: begin - if (start_i) begin - request_d.addr = request_i.addr; - request_d.size = request_i.size; - request_d.atomic_op = request_i.atomic_op; - request_d.atomic_return = request_i.atomic_return; - response_d = request_valid_i ? hyperbus_pkg::HyperRespOkay : - hyperbus_pkg::HyperRespAtomicError; - state_d = WaitWriteData; + if (atomic_started) begin + request_d = request_in; + response_d = initial_response; + state_d = WaitWriteData; end end + // Buffer the operand, or drain an invalid request through its final beat. WaitWriteData: begin - if (host_w_valid_i && host_w_ready_o) begin + if (operand_accepted) begin operand_d = host_w_i.data; - if (response_q == hyperbus_pkg::HyperRespOkay) begin - state_d = ReadCommand; - end else if (host_w_i.last) begin - read_pending_d = request_q.atomic_return; - write_pending_d = 1'b1; - state_d = ReturnResponse; - end + end + if (valid_operand_accepted) begin + state_d = ReadCommand; + end + if (invalid_operand_completed) begin + read_pending_d = request_q.atomic_return; + write_pending_d = 1'b1; + state_d = ReturnResponse; end end - ReadCommand: if (command_valid_o && command_ready_i) state_d = ReadData; + // Issue the backend read for the target word. + ReadCommand: begin + if (command_accepted) begin + state_d = ReadData; + end + end + // Capture the old value and stop early on a backend read error. ReadData: begin - if (read_valid_i && read_ready_o) begin + if (backend_read_accepted) begin read_data_d = read_i.data; - if (read_i.resp == hyperbus_pkg::HyperRespOkay) begin - state_d = WriteCommand; - end else begin - response_d = read_i.resp; - read_pending_d = request_q.atomic_return; - write_pending_d = 1'b1; - state_d = ReturnResponse; - end + end + if (backend_read_succeeded) begin + state_d = WriteCommand; + end + if (backend_read_failed) begin + response_d = read_i.resp; + read_pending_d = request_q.atomic_return; + write_pending_d = 1'b1; + state_d = ReturnResponse; + end + end + // Issue the backend write for the updated word. + WriteCommand: begin + if (command_accepted) begin + state_d = WriteData; end end - WriteCommand: if (command_valid_o && command_ready_i) state_d = WriteData; - WriteData: if (write_valid_o && write_ready_i) state_d = WriteResponse; + // Transfer the ALU result and byte strobes. + WriteData: begin + if (backend_write_accepted) begin + state_d = WriteResponse; + end + end + // Collect the backend write status. WriteResponse: begin - if (write_rsp_valid_i && write_rsp_ready_o) begin - response_d = write_rsp_error_i ? hyperbus_pkg::HyperRespAccessError : - hyperbus_pkg::HyperRespOkay; + if (backend_write_rsp_accepted) begin + response_d = backend_write_response; read_pending_d = request_q.atomic_return; write_pending_d = 1'b1; - state_d = ReturnResponse; + state_d = ReturnResponse; end end + // Hold the host read and write responses until independently accepted. ReturnResponse: begin - if (host_r_valid_o && host_r_ready_i) read_pending_d = 1'b0; - if (host_wrsp_valid_o && host_wrsp_ready_i) write_pending_d = 1'b0; - if (completed_o) state_d = Idle; + if (host_read_accepted) begin + read_pending_d = 1'b0; + end + if (host_write_rsp_accepted) begin + write_pending_d = 1'b0; + end + if (completed_o) begin + state_d = Idle; + end end default: state_d = Idle; endcase end + ///////////////////// + // State registers // + ///////////////////// `FFARN(state_q, state_d, Idle, clk_i, rst_ni) `FFARN(request_q, request_d, '0, clk_i, rst_ni) `FFARN(operand_q, operand_d, '0, clk_i, rst_ni) diff --git a/src/hyperbus_axi.sv b/src/hyperbus_axi_frontend.sv similarity index 60% rename from src/hyperbus_axi.sv rename to src/hyperbus_axi_frontend.sv index 74282b5..1fcff28 100644 --- a/src/hyperbus_axi.sv +++ b/src/hyperbus_axi_frontend.sv @@ -29,8 +29,6 @@ module hyperbus_axi_frontend #( input host_rsp_t host_rsp_i ); - localparam int unsigned AxiDataBytes = AxiDataWidth / 8; - `ASSERT_INIT(AxiAddrWidthValid, AxiAddrWidth >= 1) `ASSERT_INIT(AxiDataWidthValid, AxiDataWidth >= 16 && AxiDataWidth <= 1024 && @@ -39,13 +37,6 @@ module hyperbus_axi_frontend #( `ASSERT_INIT(AxiUserWidthValid, AxiUserWidth >= 1) typedef logic [AxiAddrWidth-1:0] axi_addr_t; - typedef logic [AxiDataWidth-1:0] axi_data_t; - typedef logic [AxiIdWidth-1:0] axi_id_t; - typedef logic [AxiDataBytes-1:0] axi_strb_t; - typedef logic [AxiUserWidth-1:0] axi_user_t; - - `AXI_TYPEDEF_ALL_CT(axi_fifo, axi_fifo_req, axi_fifo_rsp, axi_addr_t, axi_id_t, - axi_data_t, axi_strb_t, axi_user_t) // IDs stay in the AXI serializer; the neutral stream is single-outstanding. typedef struct packed { @@ -59,39 +50,30 @@ module hyperbus_axi_frontend #( typedef struct packed { axi_ax_t ax_data; logic write; - } ax_channel_spill_t; + } ax_channel_t; - typedef struct packed { - axi_strb_t strb; - axi_data_t data; - axi_user_t user; - logic last; - } axi_w_chan_t; - - axi_req_t fifo_in_req; - axi_rsp_t fifo_in_rsp; - axi_req_t fifo_out_req; - axi_rsp_t fifo_out_rsp; + /////////////////////////// + // Serialized AXI stream // + /////////////////////////// + + axi_req_t ser_in_req; + axi_rsp_t ser_in_rsp; axi_req_t ser_out_req; axi_rsp_t ser_out_rsp; - axi_ax_t ser_out_req_aw; - axi_ax_t ser_out_req_ar; - axi_ax_t rr_out_req_ax; - axi_ax_t spill_rr_out_req_ax; + axi_ax_t ser_out_req_aw; + axi_ax_t ser_out_req_ar; + ax_channel_t ser_out_req_aw_channel; + ax_channel_t ser_out_req_ar_channel; + ax_channel_t arbitrated_ax; + ax_channel_t selected_ax; - ax_channel_spill_t spill_ax_channel_in; - ax_channel_spill_t spill_ax_channel_out; + logic ax_arb_valid; + logic ax_arb_ready; - axi_w_chan_t w_data_fifo; - axi_w_chan_t w_data_fifo_in; - - logic spill_ax_valid; - logic spill_ax_ready; - logic spill_rr_out_req_write; - logic rr_out_req_write; - logic w_data_valid; - logic w_data_ready; + ////////////////////// + // Drain accounting // + ////////////////////// localparam int unsigned PendingWidth = 8; typedef logic [PendingWidth-1:0] pending_cnt_t; @@ -116,15 +98,15 @@ module hyperbus_axi_frontend #( allow_w = w_partial_q || (write_balance_q > 0); end - fifo_in_req = axi_req_i; - fifo_in_req.ar_valid = axi_req_i.ar_valid && !drain_i; - fifo_in_req.aw_valid = axi_req_i.aw_valid && allow_aw; - fifo_in_req.w_valid = axi_req_i.w_valid && allow_w; + ser_in_req = axi_req_i; + ser_in_req.ar_valid = axi_req_i.ar_valid && !drain_i; + ser_in_req.aw_valid = axi_req_i.aw_valid && allow_aw; + ser_in_req.w_valid = axi_req_i.w_valid && allow_w; - axi_rsp_o = fifo_in_rsp; - axi_rsp_o.ar_ready = fifo_in_rsp.ar_ready && !drain_i; - axi_rsp_o.aw_ready = fifo_in_rsp.aw_ready && allow_aw; - axi_rsp_o.w_ready = fifo_in_rsp.w_ready && allow_w; + axi_rsp_o = ser_in_rsp; + axi_rsp_o.ar_ready = ser_in_rsp.ar_ready && !drain_i; + axi_rsp_o.aw_ready = ser_in_rsp.aw_ready && allow_aw; + axi_rsp_o.w_ready = ser_in_rsp.w_ready && allow_w; end assign axi_ar_accepted = axi_req_i.ar_valid && axi_rsp_o.ar_ready; @@ -170,37 +152,21 @@ module hyperbus_axi_frontend #( assign idle_o = (read_pending_q == '0) && (write_pending_q == '0) && (write_balance_q == '0) && !w_partial_q; - axi_fifo #( - .Depth ( 8 ), - .FallThrough ( 1'b0 ), - .aw_chan_t ( axi_fifo_aw_chan_t ), - .w_chan_t ( axi_fifo_w_chan_t ), - .b_chan_t ( axi_fifo_b_chan_t ), - .ar_chan_t ( axi_fifo_ar_chan_t ), - .r_chan_t ( axi_fifo_r_chan_t ), - .axi_req_t ( axi_req_t ), - .axi_resp_t ( axi_rsp_t ) - ) i_axi_fifo ( - .clk_i, - .rst_ni, - .test_i ( 1'b0 ), - .slv_req_i ( fifo_in_req ), - .slv_resp_o ( fifo_in_rsp ), - .mst_req_o ( fifo_out_req ), - .mst_resp_i ( fifo_out_rsp ) - ); + ///////////////////////// + // Command arbitration // + ///////////////////////// axi_serializer #( - .MaxReadTxns ( 1 ), - .MaxWriteTxns ( 1 ), + .MaxReadTxns ( 4 ), + .MaxWriteTxns ( 4 ), .AxiIdWidth ( AxiIdWidth ), .axi_req_t ( axi_req_t ), .axi_resp_t ( axi_rsp_t ) ) i_axi_serializer ( .clk_i, .rst_ni, - .slv_req_i ( fifo_out_req ), - .slv_resp_o ( fifo_out_rsp ), + .slv_req_i ( ser_in_req ), + .slv_resp_o ( ser_in_rsp ), .mst_req_o ( ser_out_req ), .mst_resp_i ( ser_out_rsp ) ); @@ -217,65 +183,80 @@ module hyperbus_axi_frontend #( assign ser_out_req_aw.size = ser_out_req.aw.size; assign ser_out_req_aw.atop = ser_out_req.aw.atop; + assign ser_out_req_ar_channel = '{ax_data: ser_out_req_ar, write: 1'b0}; + assign ser_out_req_aw_channel = '{ax_data: ser_out_req_aw, write: 1'b1}; + rr_arb_tree #( - .NumIn ( 2 ), - .DataType ( axi_ax_t ), - .AxiVldRdy ( 1 ), - .ExtPrio ( 1'b1 ) + .NumIn ( 2 ), + .DataType ( ax_channel_t ), + .AxiVldRdy ( 1'b1 ) ) i_rr_arb_tree_ax ( .clk_i, .rst_ni, - .flush_i ( 1'b0 ), - .rr_i ( '0 ), + .flush_i ( 1'b0 ), + .rr_i ( '0 ), .req_i ( {ser_out_req.aw_valid, ser_out_req.ar_valid} ), .gnt_o ( {ser_out_rsp.aw_ready, ser_out_rsp.ar_ready} ), - .data_i ( {ser_out_req_aw, ser_out_req_ar} ), - .req_o ( spill_ax_valid ), - .gnt_i ( spill_ax_ready ), - .data_o ( spill_rr_out_req_ax ), - .idx_o ( spill_rr_out_req_write ) + .data_i ( {ser_out_req_aw_channel, ser_out_req_ar_channel} ), + .req_o ( ax_arb_valid ), + .gnt_i ( ax_arb_ready ), + .data_o ( arbitrated_ax ), + .idx_o ( ) ); - assign spill_ax_channel_in.ax_data = spill_rr_out_req_ax; - assign spill_ax_channel_in.write = spill_rr_out_req_write; - - spill_register #( - .T ( ax_channel_spill_t ) - ) i_ax_spill_register ( + stream_register #( + .T ( ax_channel_t ) + ) i_ax_register ( .clk_i, .rst_ni, - .valid_i ( spill_ax_valid ), - .ready_o ( spill_ax_ready ), - .data_i ( spill_ax_channel_in ), - .valid_o ( host_req_o.cmd_valid ), - .ready_i ( host_rsp_i.cmd_ready ), - .data_o ( spill_ax_channel_out ) + .clr_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .valid_i ( ax_arb_valid ), + .ready_o ( ax_arb_ready ), + .data_i ( arbitrated_ax ), + .valid_o ( host_req_o.cmd_valid ), + .ready_i ( host_rsp_i.cmd_ready ), + .data_o ( selected_ax ) ); - assign rr_out_req_ax = spill_ax_channel_out.ax_data; - assign rr_out_req_write = spill_ax_channel_out.write; + ////////////////////////// + // Host command mapping // + ////////////////////////// - assign host_req_o.cmd.write = rr_out_req_write; - assign host_req_o.cmd.addr = rr_out_req_ax.addr; - assign host_req_o.cmd.beats = hyperbus_pkg::hyper_blen_t'(rr_out_req_ax.len) + + assign host_req_o.cmd.write = selected_ax.write; + assign host_req_o.cmd.addr = selected_ax.ax_data.addr; + assign host_req_o.cmd.beats = hyperbus_pkg::hyper_blen_t'(selected_ax.ax_data.len) + hyperbus_pkg::hyper_blen_t'(1); - assign host_req_o.cmd.size = rr_out_req_ax.size; - assign host_req_o.cmd.burst = (rr_out_req_ax.burst == axi_pkg::BURST_FIXED) ? + assign host_req_o.cmd.size = selected_ax.ax_data.size; + assign host_req_o.cmd.burst = (selected_ax.ax_data.burst == axi_pkg::BURST_FIXED) ? hyperbus_pkg::HyperBurstFixed : hyperbus_pkg::HyperBurstIncr; + + logic atomic_arithmetic_class; + logic atomic_endian_sensitive; + logic atomic_big_endian_unsupported; + + assign atomic_arithmetic_class = + (selected_ax.ax_data.atop[5:4] == axi_pkg::ATOP_ATOMICSTORE) || + (selected_ax.ax_data.atop[5:4] == axi_pkg::ATOP_ATOMICLOAD); + assign atomic_endian_sensitive = + (selected_ax.ax_data.atop[2:0] == axi_pkg::ATOP_ADD) || + (selected_ax.ax_data.atop[2:0] >= axi_pkg::ATOP_SMAX); + assign atomic_big_endian_unsupported = + selected_ax.ax_data.atop[3] && atomic_endian_sensitive; + always_comb begin : proc_atomic_decode - host_req_o.cmd.atomic_op = (rr_out_req_ax.atop == '0) ? + host_req_o.cmd.atomic_op = (selected_ax.ax_data.atop == '0) ? hyperbus_pkg::HyperAtomicNone : hyperbus_pkg::HyperAtomicInvalid; - unique case (rr_out_req_ax.atop) + unique case (selected_ax.ax_data.atop) axi_pkg::ATOP_ATOMICSWAP: host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicSwap; axi_pkg::ATOP_ATOMICCMP: host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicCompare; default: begin - if ((rr_out_req_ax.atop[5:4] == axi_pkg::ATOP_ATOMICSTORE) || - (rr_out_req_ax.atop[5:4] == axi_pkg::ATOP_ATOMICLOAD)) begin - unique case (rr_out_req_ax.atop[2:0]) + if (atomic_arithmetic_class) begin + unique case (selected_ax.ax_data.atop[2:0]) axi_pkg::ATOP_ADD: host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicAdd; axi_pkg::ATOP_CLR: @@ -295,46 +276,29 @@ module hyperbus_axi_frontend #( default:; endcase // AXI defines bit 3 as endianness for arithmetic atomics. - if (rr_out_req_ax.atop[3] && - ((rr_out_req_ax.atop[2:0] == axi_pkg::ATOP_ADD) || - (rr_out_req_ax.atop[2:0] >= axi_pkg::ATOP_SMAX))) begin + if (atomic_big_endian_unsupported) begin host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicInvalid; end end end endcase - host_req_o.cmd.atomic_return = rr_out_req_ax.atop[axi_pkg::ATOP_R_RESP]; - host_req_o.cmd.ordered = rr_out_req_ax.atop != '0; + host_req_o.cmd.atomic_return = selected_ax.ax_data.atop[axi_pkg::ATOP_R_RESP]; + host_req_o.cmd.ordered = selected_ax.ax_data.atop != '0; end - assign w_data_fifo_in.data = ser_out_req.w.data; - assign w_data_fifo_in.strb = ser_out_req.w.strb; - assign w_data_fifo_in.last = ser_out_req.w.last; - assign w_data_fifo_in.user = ser_out_req.w.user; + /////////////////////// + // Host write stream // + /////////////////////// - stream_fifo #( - .FALL_THROUGH ( 1'b0 ), - .T ( axi_w_chan_t ), - .DEPTH ( 16 ) - ) i_wchan_stream_fifo ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o ( ), - .data_i ( w_data_fifo_in ), - .valid_i ( ser_out_req.w_valid ), - .ready_o ( ser_out_rsp.w_ready ), - .data_o ( w_data_fifo ), - .valid_o ( w_data_valid ), - .ready_i ( w_data_ready ) - ); + assign host_req_o.w.data = ser_out_req.w.data; + assign host_req_o.w.strb = ser_out_req.w.strb; + assign host_req_o.w.last = ser_out_req.w.last; + assign host_req_o.w_valid = ser_out_req.w_valid; + assign ser_out_rsp.w_ready = host_rsp_i.w_ready; - assign host_req_o.w.data = w_data_fifo.data; - assign host_req_o.w.strb = w_data_fifo.strb; - assign host_req_o.w.last = w_data_fifo.last; - assign host_req_o.w_valid = w_data_valid; - assign w_data_ready = host_rsp_i.w_ready; + ////////////////////// + // Response mapping // + ////////////////////// assign ser_out_rsp.r.data = host_rsp_i.r.data; assign ser_out_rsp.r.last = host_rsp_i.r.last; @@ -366,8 +330,9 @@ module hyperbus_axi_frontend #( assign host_req_o.wrsp_ready = ser_out_req.b_ready; `ASSERT(AxiBurstType, (host_req_o.cmd_valid && host_rsp_i.cmd_ready) |-> - ((rr_out_req_ax.burst == axi_pkg::BURST_INCR) || - ((rr_out_req_ax.burst == axi_pkg::BURST_FIXED) && (rr_out_req_ax.len == '0)))) + ((selected_ax.ax_data.burst == axi_pkg::BURST_INCR) || + ((selected_ax.ax_data.burst == axi_pkg::BURST_FIXED) && + (selected_ax.ax_data.len == '0)))) `ASSERT(ReadResponsePending, axi_r_completed |-> (read_pending_q != '0)) `ASSERT(WriteResponsePending, axi_b_accepted |-> (write_pending_q != '0)) diff --git a/src/hyperbus_cfg_frontend.sv b/src/hyperbus_cfg_frontend.sv index 3465bbc..e574d74 100644 --- a/src/hyperbus_cfg_frontend.sv +++ b/src/hyperbus_cfg_frontend.sv @@ -6,7 +6,6 @@ `include "common_cells/assertions.svh" module hyperbus_cfg_frontend #( - parameter int unsigned NumChips = -1, parameter int unsigned NumPhys = 2, parameter type reg_req_t = logic, parameter type reg_rsp_t = logic, @@ -34,7 +33,7 @@ module hyperbus_cfg_frontend #( input logic clock_div_apply_done_i, output hyperbus_pkg::frontend_cfg_t frontend_cfg_o, - output host_rule_t [NumChips-1:0] chip_rules_o, + output host_rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_o, input logic decode_error_i ); @@ -49,6 +48,10 @@ module hyperbus_cfg_frontend #( CfgPhyWaitAck } cfg_state_e; + ////////////////////// + // Persistent state // + ////////////////////// + cfg_state_e cfg_state_d; cfg_state_e cfg_state_q; hyperbus_pkg::phy_cfg_t phy_cfg; @@ -62,12 +65,18 @@ module hyperbus_cfg_frontend #( logic cfg_write_pending_d; logic cfg_write_pending_q; - `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= 8) `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) `ASSERT_INIT(RegDataWidthValid, RegDataWidth == 32) - reg_req_t cfg_reg_req; - reg_rsp_t cfg_reg_rsp; + ////////////////////////// + // Configuration changes // + ////////////////////////// + + logic cfg_write_requested; + logic drain_completed; + logic cfg_write_accepted; + logic clock_apply_accepted; + logic phy_apply_accepted; assign phy_cfg_changed = phy_cfg != cfg_applied_q; assign clock_div_changed = frontend_cfg_o.phy_clock_div != clock_div_applied_q; @@ -77,6 +86,18 @@ module hyperbus_cfg_frontend #( assign cfg_apply_o = phy_cfg; assign clock_div_apply_valid_o = cfg_state_q == CfgClockSend; assign clock_div_apply_o = frontend_cfg_o.phy_clock_div; + assign cfg_write_requested = reg_req_i.valid && reg_req_i.write; + assign drain_completed = host_idle_i && !trans_active_i; + assign cfg_write_accepted = cfg_reg_req.valid && cfg_reg_rsp.ready; + assign clock_apply_accepted = clock_div_apply_valid_o && clock_div_apply_ready_i; + assign phy_apply_accepted = cfg_apply_valid_o && cfg_apply_ready_i; + + //////////////////////////// + // Register access gating // + //////////////////////////// + + reg_req_t cfg_reg_req; + reg_rsp_t cfg_reg_rsp; always_comb begin : proc_cfg_reg_gate cfg_reg_req = reg_req_i; @@ -97,6 +118,12 @@ module hyperbus_cfg_frontend #( end end + ///////////////////////////////// + // Atomic configuration update // + ///////////////////////////////// + + // Drain host traffic before committing a register write or applying changed + // clock and PHY values. Each destination acknowledges before traffic resumes. always_comb begin : proc_cfg_apply cfg_state_d = cfg_state_q; cfg_applied_d = cfg_applied_q; @@ -104,26 +131,30 @@ module hyperbus_cfg_frontend #( cfg_write_pending_d = cfg_write_pending_q; unique case (cfg_state_q) + // Detect pending register or applied-configuration changes. CfgIdle: begin if (cfg_changed) begin cfg_write_pending_d = 1'b0; cfg_state_d = CfgDrain; - end else if (reg_req_i.valid && reg_req_i.write) begin + end else if (cfg_write_requested) begin cfg_write_pending_d = 1'b1; cfg_state_d = CfgDrain; end end + // Stop admission and wait until all accepted traffic has completed. CfgDrain: begin - if (host_idle_i && !trans_active_i) begin + if (drain_completed) begin cfg_state_d = cfg_write_pending_q ? CfgCommit : CfgObserve; end end + // Commit exactly one blocked register write while traffic remains drained. CfgCommit: begin - if (cfg_reg_rsp.ready) begin + if (cfg_write_accepted) begin cfg_write_pending_d = 1'b0; cfg_state_d = CfgObserve; end end + // Re-evaluate generated configuration outputs after the write commits. CfgObserve: begin if (clock_div_changed) begin cfg_state_d = CfgClockSend; @@ -133,32 +164,32 @@ module hyperbus_cfg_frontend #( cfg_state_d = CfgIdle; end end + // Send a changed divider value to the isochronous clock generator. CfgClockSend: begin - if (clock_div_apply_ready_i) begin - if (clock_div_apply_done_i) begin - clock_div_applied_d = frontend_cfg_o.phy_clock_div; - cfg_state_d = phy_cfg_changed ? CfgPhySend : CfgIdle; - end else begin - cfg_state_d = CfgClockWaitAck; - end + if (clock_apply_accepted && clock_div_apply_done_i) begin + clock_div_applied_d = frontend_cfg_o.phy_clock_div; + cfg_state_d = phy_cfg_changed ? CfgPhySend : CfgIdle; + end else if (clock_apply_accepted) begin + cfg_state_d = CfgClockWaitAck; end end + // Hold the barrier until the divider confirms the update. CfgClockWaitAck: begin if (clock_div_apply_done_i) begin clock_div_applied_d = frontend_cfg_o.phy_clock_div; cfg_state_d = phy_cfg_changed ? CfgPhySend : CfgIdle; end end + // Send changed PHY configuration through the selected bridge. CfgPhySend: begin - if (cfg_apply_ready_i) begin - if (cfg_apply_done_i) begin - cfg_applied_d = phy_cfg; - cfg_state_d = CfgIdle; - end else begin - cfg_state_d = CfgPhyWaitAck; - end + if (phy_apply_accepted && cfg_apply_done_i) begin + cfg_applied_d = phy_cfg; + cfg_state_d = CfgIdle; + end else if (phy_apply_accepted) begin + cfg_state_d = CfgPhyWaitAck; end end + // Hold the barrier until the backend confirms the PHY update. CfgPhyWaitAck: begin if (cfg_apply_done_i) begin cfg_applied_d = phy_cfg; @@ -171,13 +202,20 @@ module hyperbus_cfg_frontend #( endcase end + ///////////////////// + // State registers // + ///////////////////// + `FFARN(cfg_state_q, cfg_state_d, CfgIdle, clk_i, rst_ni) `FFARN(cfg_applied_q, cfg_applied_d, '0, clk_i, rst_ni) `FFARN(clock_div_applied_q, clock_div_applied_d, '0, clk_i, rst_ni) `FFARN(cfg_write_pending_q, cfg_write_pending_d, 1'b0, clk_i, rst_ni) + /////////////////// + // Register bank // + /////////////////// + hyperbus_cfg_regs #( - .NumChips ( NumChips ), .NumPhys ( NumPhys ), .RegDataWidth ( RegDataWidth ), .reg_req_t ( reg_req_t ), diff --git a/src/hyperbus_cfg_regs.sv b/src/hyperbus_cfg_regs.sv index 778cf68..c93825c 100644 --- a/src/hyperbus_cfg_regs.sv +++ b/src/hyperbus_cfg_regs.sv @@ -7,7 +7,6 @@ `include "common_cells/assertions.svh" module hyperbus_cfg_regs #( - parameter int unsigned NumChips = -1, parameter int unsigned NumPhys = -1, parameter int unsigned RegDataWidth = -1, parameter type reg_req_t = logic, @@ -22,15 +21,13 @@ module hyperbus_cfg_regs #( output hyperbus_pkg::frontend_cfg_t frontend_cfg_o, output hyperbus_pkg::phy_cfg_t phy_cfg_o, - output addr_rule_t [NumChips-1:0] chip_rules_o, + output addr_rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_o, input decode_error_i ); - localparam int unsigned NumChipsMax = 8; localparam int unsigned NumRegs = 31; localparam int unsigned RegsBits = cf_math_pkg::idx_width(NumRegs); localparam int unsigned RegStrbWidth = RegDataWidth/8; - `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= NumChipsMax) `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) `ASSERT_INIT(RegDataWidthValid, RegDataWidth == 32) @@ -71,7 +68,7 @@ module hyperbus_cfg_regs #( hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__in_t cfg_hwif_in; hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__out_t cfg_hwif_out; - addr_rule_t [NumChipsMax-1:0] chip_rules_all; + addr_rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_all; reg_idx_t sel_reg; logic sel_reg_mapped; @@ -81,6 +78,10 @@ module hyperbus_cfg_regs #( cfg_apb_req_t cfg_apb_req; cfg_apb_rsp_t cfg_apb_rsp; + ///////////////////////////// + // Register bus adaptation // + ///////////////////////////// + assign cfg_hwif_in.status.decode_error.hwset = decode_error_i; assign sel_reg = reg_req_i.addr[$clog2(RegStrbWidth) +: RegsBits]; @@ -96,64 +97,44 @@ module hyperbus_cfg_regs #( assign cfg_reg_req.wdata = 32'(reg_req_i.wdata); assign cfg_reg_req.wstrb = cfg_strb_t'(reg_req_i.wstrb); + //////////////////////// + // Chip address rules // + //////////////////////// + + logic [hyperbus_pkg::HyperNumChips-1:0][9:0] chip_base; + logic [hyperbus_pkg::HyperNumChips-1:0][9:0] chip_bound; + + assign chip_base[0] = cfg_hwif_out.chip0_base.value.value; + assign chip_base[1] = cfg_hwif_out.chip1_base.value.value; + assign chip_base[2] = cfg_hwif_out.chip2_base.value.value; + assign chip_base[3] = cfg_hwif_out.chip3_base.value.value; + assign chip_base[4] = cfg_hwif_out.chip4_base.value.value; + assign chip_base[5] = cfg_hwif_out.chip5_base.value.value; + assign chip_base[6] = cfg_hwif_out.chip6_base.value.value; + assign chip_base[7] = cfg_hwif_out.chip7_base.value.value; + + assign chip_bound[0] = cfg_hwif_out.chip0_bound.value.value; + assign chip_bound[1] = cfg_hwif_out.chip1_bound.value.value; + assign chip_bound[2] = cfg_hwif_out.chip2_bound.value.value; + assign chip_bound[3] = cfg_hwif_out.chip3_bound.value.value; + assign chip_bound[4] = cfg_hwif_out.chip4_bound.value.value; + assign chip_bound[5] = cfg_hwif_out.chip5_bound.value.value; + assign chip_bound[6] = cfg_hwif_out.chip6_bound.value.value; + assign chip_bound[7] = cfg_hwif_out.chip7_bound.value.value; + always_comb begin : proc_chip_rules chip_rules_all = '0; - for (int unsigned i = 0; i < NumChipsMax; i++) begin - chip_rules_all[i].idx = unsigned'(i); - unique case (i) - 0: begin - chip_rules_all[i].start_addr = { - cfg_hwif_out.chip0_base.value.value, 22'b0}; - chip_rules_all[i].end_addr = { - cfg_hwif_out.chip0_bound.value.value, 22'b0}; - end - 1: begin - chip_rules_all[i].start_addr = { - cfg_hwif_out.chip1_base.value.value, 22'b0}; - chip_rules_all[i].end_addr = { - cfg_hwif_out.chip1_bound.value.value, 22'b0}; - end - 2: begin - chip_rules_all[i].start_addr = { - cfg_hwif_out.chip2_base.value.value, 22'b0}; - chip_rules_all[i].end_addr = { - cfg_hwif_out.chip2_bound.value.value, 22'b0}; - end - 3: begin - chip_rules_all[i].start_addr = { - cfg_hwif_out.chip3_base.value.value, 22'b0}; - chip_rules_all[i].end_addr = { - cfg_hwif_out.chip3_bound.value.value, 22'b0}; - end - 4: begin - chip_rules_all[i].start_addr = { - cfg_hwif_out.chip4_base.value.value, 22'b0}; - chip_rules_all[i].end_addr = { - cfg_hwif_out.chip4_bound.value.value, 22'b0}; - end - 5: begin - chip_rules_all[i].start_addr = { - cfg_hwif_out.chip5_base.value.value, 22'b0}; - chip_rules_all[i].end_addr = { - cfg_hwif_out.chip5_bound.value.value, 22'b0}; - end - 6: begin - chip_rules_all[i].start_addr = { - cfg_hwif_out.chip6_base.value.value, 22'b0}; - chip_rules_all[i].end_addr = { - cfg_hwif_out.chip6_bound.value.value, 22'b0}; - end - 7: begin - chip_rules_all[i].start_addr = { - cfg_hwif_out.chip7_base.value.value, 22'b0}; - chip_rules_all[i].end_addr = { - cfg_hwif_out.chip7_bound.value.value, 22'b0}; - end - default:; - endcase + for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips; i++) begin + chip_rules_all[i].idx = unsigned'(i); + chip_rules_all[i].start_addr = {chip_base[i], 22'b0}; + chip_rules_all[i].end_addr = {chip_bound[i], 22'b0}; end end + //////////////////////// + // Generated APB bank // + //////////////////////// + reg_to_apb #( .reg_req_t ( cfg_reg_req_t ), .reg_rsp_t ( cfg_reg_rsp_t ), @@ -169,8 +150,8 @@ module hyperbus_cfg_regs #( ); hyperbus_cfg_regblock i_cfg_regblock ( - .clk ( clk_i ), - .arst_n ( rst_ni ), + .clk ( clk_i ), + .arst_n ( rst_ni ), .s_apb_psel ( cfg_apb_req.psel ), .s_apb_penable ( cfg_apb_req.penable ), .s_apb_pwrite ( cfg_apb_req.pwrite ), @@ -181,10 +162,14 @@ module hyperbus_cfg_regs #( .s_apb_pready ( cfg_apb_rsp.pready ), .s_apb_prdata ( cfg_apb_rsp.prdata ), .s_apb_pslverr ( cfg_apb_rsp.pslverr ), - .hwif_in ( cfg_hwif_in ), - .hwif_out ( cfg_hwif_out ) + .hwif_in ( cfg_hwif_in ), + .hwif_out ( cfg_hwif_out ) ); + /////////////////////////// + // Typed configuration // + /////////////////////////// + always_comb begin : proc_cfg_output frontend_cfg_o = '0; phy_cfg_o = '0; @@ -211,13 +196,6 @@ module hyperbus_cfg_regs #( phy_cfg_o.dual_phy = (NumPhys == 2) && cfg_hwif_out.dual_phy.value.value; end - for (genvar i = 0; unsigned'(i) < NumChipsMax; i++) begin : gen_chip_rules - if (i < NumChips) begin : gen_active - assign chip_rules_o[i] = chip_rules_all[i]; - end else begin : gen_inactive - logic unused_chip_rule; - assign unused_chip_rule = ^chip_rules_all[i]; - end - end + assign chip_rules_o = chip_rules_all; endmodule : hyperbus_cfg_regs diff --git a/src/hyperbus_iso_bridge.sv b/src/hyperbus_iso_bridge.sv index 91db8fc..c751863 100644 --- a/src/hyperbus_iso_bridge.sv +++ b/src/hyperbus_iso_bridge.sv @@ -5,8 +5,6 @@ `include "common_cells/registers.svh" module hyperbus_iso_bridge #( - parameter int unsigned RxFifoLogDepth = 8, - parameter int unsigned TxFifoLogDepth = 3, parameter type hyper_rx_t = logic, parameter type hyper_tx_t = logic, parameter type hyper_wrsp_t = logic, @@ -34,32 +32,18 @@ module hyperbus_iso_bridge #( input logic cfg_apply_ready_i ); - hyperbus_pkg::phy_cfg_t cfg_apply_data_q; - logic cfg_apply_accepted; - logic cfg_apply_pending_d; - logic cfg_apply_pending_q; - hyper_cmd_t cmd_data_q; - logic cmd_accepted; - hyper_wrsp_t wrsp_data_q; - logic wrsp_accepted; - hyper_tx_t tx_fifo_data; - logic tx_fifo_valid; - logic tx_fifo_ready; - hyper_rx_t rx_src_fifo_data; - logic rx_src_fifo_valid; - logic rx_src_fifo_ready; - logic rx_iso_valid; - logic rx_iso_ready; - hyper_rx_t rx_iso_data; - hyper_rx_t rx_fifo_data; - logic rx_fifo_valid; - logic rx_fifo_ready; + //////////////////////////// + // Configuration crossing // + //////////////////////////// + + hyperbus_pkg::phy_cfg_t cfg_apply_data_q; + logic cfg_apply_accepted; + logic cfg_apply_pending_d; + logic cfg_apply_pending_q; assign cfg_apply_accepted = frontend_cfg_apply_valid_i && frontend_cfg_apply_ready_o; assign frontend_cfg_apply_done_o = cfg_apply_pending_q && frontend_cfg_apply_ready_o; - assign cmd_accepted = frontend_req_i.cmd_valid && frontend_rsp_o.cmd_ready; - assign wrsp_accepted = backend_rsp_i.wrsp_valid && backend_req_o.wrsp_ready; always_comb begin : proc_cfg_apply_pending cfg_apply_pending_d = cfg_apply_pending_q; @@ -73,8 +57,6 @@ module hyperbus_iso_bridge #( `FFLARN(cfg_apply_data_q, cfg_apply_i, cfg_apply_accepted, '0, clk_sys_i, rst_sys_ni) `FFARN(cfg_apply_pending_q, cfg_apply_pending_d, 1'b0, clk_sys_i, rst_sys_ni) - `FFLARN(cmd_data_q, frontend_req_i.cmd, cmd_accepted, '0, clk_sys_i, rst_sys_ni) - `FFLARN(wrsp_data_q, backend_rsp_i.wrsp, wrsp_accepted, '0, clk_phy_i, rst_phy_ni) isochronous_4phase_handshake i_iso_cfg_apply ( .src_clk_i ( clk_sys_i ), @@ -89,6 +71,16 @@ module hyperbus_iso_bridge #( assign cfg_apply_o = cfg_apply_data_q; + ////////////////////// + // Command crossing // + ////////////////////// + + hyper_cmd_t cmd_data_q; + logic cmd_accepted; + + assign cmd_accepted = frontend_req_i.cmd_valid && frontend_rsp_o.cmd_ready; + `FFLARN(cmd_data_q, frontend_req_i.cmd, cmd_accepted, '0, clk_sys_i, rst_sys_ni) + isochronous_4phase_handshake i_iso_cmd ( .src_clk_i ( clk_sys_i ), .src_rst_ni ( rst_sys_ni ), @@ -102,6 +94,16 @@ module hyperbus_iso_bridge #( assign backend_req_o.cmd = cmd_data_q; + ///////////////////////////// + // Write-response crossing // + ///////////////////////////// + + hyper_wrsp_t wrsp_data_q; + logic wrsp_accepted; + + assign wrsp_accepted = backend_rsp_i.wrsp_valid && backend_req_o.wrsp_ready; + `FFLARN(wrsp_data_q, backend_rsp_i.wrsp, wrsp_accepted, '0, clk_phy_i, rst_phy_ni) + isochronous_4phase_handshake i_iso_wrsp ( .src_clk_i ( clk_phy_i ), .src_rst_ni ( rst_phy_ni ), @@ -115,32 +117,18 @@ module hyperbus_iso_bridge #( assign frontend_rsp_o.wrsp = wrsp_data_q; - stream_fifo #( - .FALL_THROUGH ( 1'b0 ), - .DEPTH ( 1 << TxFifoLogDepth ), - .T ( hyper_tx_t ) - ) i_tx_fifo ( - .clk_i ( clk_sys_i ), - .rst_ni ( rst_sys_ni ), - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o ( ), - .data_i ( frontend_req_i.tx ), - .valid_i ( frontend_req_i.tx_valid ), - .ready_o ( frontend_rsp_o.tx_ready ), - .data_o ( tx_fifo_data ), - .valid_o ( tx_fifo_valid ), - .ready_i ( tx_fifo_ready ) - ); + /////////////////////// + // TX data crossing // + /////////////////////// isochronous_spill_register #( .T ( hyper_tx_t ) ) i_iso_tx ( .src_clk_i ( clk_sys_i ), .src_rst_ni ( rst_sys_ni ), - .src_valid_i ( tx_fifo_valid ), - .src_ready_o ( tx_fifo_ready ), - .src_data_i ( tx_fifo_data ), + .src_valid_i ( frontend_req_i.tx_valid ), + .src_ready_o ( frontend_rsp_o.tx_ready ), + .src_data_i ( frontend_req_i.tx ), .dst_clk_i ( clk_phy_i ), .dst_rst_ni ( rst_phy_ni ), .dst_valid_o ( backend_req_o.tx_valid ), @@ -148,59 +136,23 @@ module hyperbus_iso_bridge #( .dst_data_o ( backend_req_o.tx ) ); - stream_fifo #( - .FALL_THROUGH ( 1'b0 ), - .DEPTH ( 1 << RxFifoLogDepth ), - .T ( hyper_rx_t ) - ) i_rx_src_fifo ( - .clk_i ( clk_phy_i ), - .rst_ni ( rst_phy_ni ), - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o ( ), - .data_i ( backend_rsp_i.rx ), - .valid_i ( backend_rsp_i.rx_valid ), - .ready_o ( backend_req_o.rx_ready ), - .data_o ( rx_src_fifo_data ), - .valid_o ( rx_src_fifo_valid ), - .ready_i ( rx_src_fifo_ready ) - ); + /////////////////////// + // RX data crossing // + /////////////////////// isochronous_spill_register #( .T ( hyper_rx_t ) ) i_iso_rx ( .src_clk_i ( clk_phy_i ), .src_rst_ni ( rst_phy_ni ), - .src_valid_i ( rx_src_fifo_valid ), - .src_ready_o ( rx_src_fifo_ready ), - .src_data_i ( rx_src_fifo_data ), + .src_valid_i ( backend_rsp_i.rx_valid ), + .src_ready_o ( backend_req_o.rx_ready ), + .src_data_i ( backend_rsp_i.rx ), .dst_clk_i ( clk_sys_i ), .dst_rst_ni ( rst_sys_ni ), - .dst_valid_o ( rx_iso_valid ), - .dst_ready_i ( rx_iso_ready ), - .dst_data_o ( rx_iso_data ) - ); - - stream_fifo #( - .FALL_THROUGH ( 1'b0 ), - .DEPTH ( 1 << RxFifoLogDepth ), - .T ( hyper_rx_t ) - ) i_rx_fifo ( - .clk_i ( clk_sys_i ), - .rst_ni ( rst_sys_ni ), - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o ( ), - .data_i ( rx_iso_data ), - .valid_i ( rx_iso_valid ), - .ready_o ( rx_iso_ready ), - .data_o ( rx_fifo_data ), - .valid_o ( rx_fifo_valid ), - .ready_i ( rx_fifo_ready ) + .dst_valid_o ( frontend_rsp_o.rx_valid ), + .dst_ready_i ( frontend_req_i.rx_ready ), + .dst_data_o ( frontend_rsp_o.rx ) ); - assign frontend_rsp_o.rx = rx_fifo_data; - assign frontend_rsp_o.rx_valid = rx_fifo_valid; - assign rx_fifo_ready = frontend_req_i.rx_ready; - endmodule diff --git a/src/hyperbus_isochronous.sv b/src/hyperbus_isochronous.sv index a08551e..eeb4da8 100644 --- a/src/hyperbus_isochronous.sv +++ b/src/hyperbus_isochronous.sv @@ -7,22 +7,21 @@ `include "common_cells/registers.svh" module hyperbus_isochronous #( - parameter int unsigned NumChips = -1, - parameter int unsigned NumPhys = 2, - parameter int unsigned AxiAddrWidth = -1, - parameter int unsigned AxiDataWidth = -1, - parameter int unsigned AxiIdWidth = -1, - parameter int unsigned AxiUserWidth = -1, - parameter type axi_req_t = logic, - parameter type axi_rsp_t = logic, - parameter int unsigned RegDataWidth = -1, - parameter type reg_req_t = logic, - parameter type reg_rsp_t = logic, - parameter type axi_rule_t = logic, - parameter int unsigned RxFifoLogDepth = 8, - parameter int unsigned TxFifoLogDepth = 3, - parameter int unsigned PhyStartupCycles = 300 * 200, - parameter int unsigned SyncStages = 2 + parameter int unsigned NumPhys = 2, + parameter int unsigned AxiAddrWidth = -1, + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned AxiIdWidth = -1, + parameter int unsigned AxiUserWidth = -1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter int unsigned RegDataWidth = -1, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type axi_rule_t = logic, + parameter int unsigned HostCommandDepth = 8, + parameter int unsigned HostWriteBufferBytes = 128, + parameter int unsigned PhyStartupCycles = 300 * 200, + parameter int unsigned SyncStages = 2 ) ( input logic clk_sys_i, input logic rst_sys_ni, @@ -37,7 +36,7 @@ module hyperbus_isochronous #( input reg_req_t reg_req_i, output reg_rsp_t reg_rsp_o, - output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, + output logic [NumPhys-1:0][hyperbus_pkg::HyperNumChips-1:0] hyper_cs_no, output logic [NumPhys-1:0] hyper_ck_o, output logic [NumPhys-1:0] hyper_ck_no, output logic [NumPhys-1:0] hyper_rwds_o, @@ -55,10 +54,19 @@ module hyperbus_isochronous #( typedef logic [AxiDataWidth-1:0] host_data_t; typedef logic [AxiDataWidth/8-1:0] host_strb_t; `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) - `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys, NumChips) + `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys) + + ///////////////////// + // Clock and reset // + ///////////////////// + + logic clk_backend; + logic rst_backend_n; + + //////////////////////// + // Configuration path // + //////////////////////// - logic clk_backend; - logic rst_backend_n; hyperbus_pkg::phy_cfg_t frontend_cfg_apply; logic frontend_cfg_apply_valid; logic frontend_cfg_apply_ready; @@ -74,7 +82,11 @@ module hyperbus_isochronous #( logic backend_cfg_apply_ready; hyperbus_pkg::frontend_cfg_t frontend_cfg; - axi_rule_t [NumChips-1:0] frontend_chip_rules; + axi_rule_t [hyperbus_pkg::HyperNumChips-1:0] frontend_chip_rules; + + //////////////////// + // Dataflow links // + //////////////////// host_req_t host_req; host_rsp_t host_rsp; @@ -85,6 +97,10 @@ module hyperbus_isochronous #( hyper_req_t backend_req; hyper_rsp_t backend_rsp; + ////////////////////////// + // Backend clock divider // + ////////////////////////// + logic clock_div_req_valid; logic clock_div_update_accepted; logic clock_div_update_pending_d; @@ -147,8 +163,11 @@ module hyperbus_isochronous #( .init_no ( ) ); + //////////////////////////// + // Configuration frontend // + //////////////////////////// + hyperbus_cfg_frontend #( - .NumChips ( NumChips ), .NumPhys ( NumPhys ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), @@ -175,6 +194,10 @@ module hyperbus_isochronous #( .decode_error_i ( midend_decode_error ) ); + ////////////////// + // AXI frontend // + ////////////////// + hyperbus_axi_frontend #( .AxiDataWidth ( AxiDataWidth ), .AxiAddrWidth ( AxiAddrWidth ), @@ -195,23 +218,28 @@ module hyperbus_isochronous #( .host_rsp_i ( host_rsp ) ); + //////////// + // Midend // + //////////// + hyperbus_midend #( - .HostAddrWidth ( AxiAddrWidth ), - .HostDataWidth ( AxiDataWidth ), - .NumChips ( NumChips ), - .NumPhys ( NumPhys ), - .host_cmd_t ( host_cmd_t ), - .host_w_t ( host_w_t ), - .host_r_t ( host_r_t ), - .host_wrsp_t ( host_wrsp_t ), - .host_req_t ( host_req_t ), - .host_rsp_t ( host_rsp_t ), - .hyper_rx_t ( hyper_rx_t ), - .hyper_tx_t ( hyper_tx_t ), - .hyper_cmd_t ( hyper_cmd_t ), - .hyper_req_t ( hyper_req_t ), - .hyper_rsp_t ( hyper_rsp_t ), - .rule_t ( axi_rule_t ) + .HostAddrWidth ( AxiAddrWidth ), + .HostDataWidth ( AxiDataWidth ), + .NumPhys ( NumPhys ), + .HostCommandDepth ( HostCommandDepth ), + .HostWriteBufferBytes ( HostWriteBufferBytes ), + .host_cmd_t ( host_cmd_t ), + .host_w_t ( host_w_t ), + .host_r_t ( host_r_t ), + .host_wrsp_t ( host_wrsp_t ), + .host_req_t ( host_req_t ), + .host_rsp_t ( host_rsp_t ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .hyper_cmd_t ( hyper_cmd_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ), + .rule_t ( axi_rule_t ) ) i_midend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), @@ -225,9 +253,11 @@ module hyperbus_isochronous #( .hyper_link_rsp_i ( midend_rsp ) ); + //////////////////////// + // Isochronous bridge // + //////////////////////// + hyperbus_iso_bridge #( - .RxFifoLogDepth ( RxFifoLogDepth ), - .TxFifoLogDepth ( TxFifoLogDepth ), .hyper_rx_t ( hyper_rx_t ), .hyper_tx_t ( hyper_tx_t ), .hyper_wrsp_t ( hyper_wrsp_t ), @@ -252,8 +282,11 @@ module hyperbus_isochronous #( .cfg_apply_ready_i ( backend_cfg_apply_ready ) ); + ///////////// + // Backend // + ///////////// + hyperbus_backend #( - .NumChips ( NumChips ), .NumPhys ( NumPhys ), .StartupCycles ( PhyStartupCycles ), .SyncStages ( SyncStages ), diff --git a/src/hyperbus_midend.sv b/src/hyperbus_midend.sv index 73f78cd..8e2702f 100644 --- a/src/hyperbus_midend.sv +++ b/src/hyperbus_midend.sv @@ -6,22 +6,23 @@ `include "common_cells/assertions.svh" module hyperbus_midend #( - parameter int unsigned HostAddrWidth = -1, - parameter int unsigned HostDataWidth = -1, - parameter int unsigned NumChips = -1, - parameter int unsigned NumPhys = -1, - parameter type host_cmd_t = logic, - parameter type host_w_t = logic, - parameter type host_r_t = logic, - parameter type host_wrsp_t = logic, - parameter type host_req_t = logic, - parameter type host_rsp_t = logic, - parameter type hyper_rx_t = logic, - parameter type hyper_tx_t = logic, - parameter type hyper_cmd_t = logic, - parameter type hyper_req_t = logic, - parameter type hyper_rsp_t = logic, - parameter type rule_t = logic + parameter int unsigned HostAddrWidth = -1, + parameter int unsigned HostDataWidth = -1, + parameter int unsigned NumPhys = -1, + parameter int unsigned HostCommandDepth = 8, + parameter int unsigned HostWriteBufferBytes = 128, + parameter type host_cmd_t = logic, + parameter type host_w_t = logic, + parameter type host_r_t = logic, + parameter type host_wrsp_t = logic, + parameter type host_req_t = logic, + parameter type host_rsp_t = logic, + parameter type hyper_rx_t = logic, + parameter type hyper_tx_t = logic, + parameter type hyper_cmd_t = logic, + parameter type hyper_req_t = logic, + parameter type hyper_rsp_t = logic, + parameter type rule_t = logic ) ( input logic clk_i, input logic rst_ni, @@ -30,7 +31,7 @@ module hyperbus_midend #( output host_rsp_t host_link_rsp_o, input hyperbus_pkg::frontend_cfg_t frontend_cfg_i, - input rule_t [NumChips-1:0] chip_rules_i, + input rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_i, output logic trans_active_o, output logic decode_error_o, @@ -38,24 +39,44 @@ module hyperbus_midend #( input hyper_rsp_t hyper_link_rsp_i ); - localparam int unsigned HostDataBytes = HostDataWidth / 8; - localparam int unsigned HostBusAddrWidth = $clog2(HostDataBytes); - localparam int unsigned PhyDataWidth = NumPhys * 16; - localparam int unsigned ChipSelWidth = cf_math_pkg::idx_width(NumChips); + localparam int unsigned HostDataBytes = HostDataWidth / 8; + localparam int unsigned HostBusAddrWidth = $clog2(HostDataBytes); + localparam int unsigned PhyDataWidth = NumPhys * 16; + localparam int unsigned WriteFifoDepth = HostWriteBufferBytes / HostDataBytes; + localparam int unsigned ReadFifoDepth = 4; + localparam int unsigned WriteRspFifoDepth = 4; + localparam int unsigned ChipSelWidth = + cf_math_pkg::idx_width(hyperbus_pkg::HyperNumChips); - `ASSERT_INIT(NumChipsValid, NumChips >= 1 && NumChips <= 8) `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) `ASSERT_INIT(HostAddrWidthValid, HostAddrWidth >= HostBusAddrWidth) `ASSERT_INIT(HostDataWidthValid, HostDataWidth >= PhyDataWidth && HostDataWidth <= 1024 && (HostDataWidth & (HostDataWidth - 1)) == 0 && (HostDataWidth % PhyDataWidth) == 0) + `ASSERT_INIT(HostCommandDepthValid, HostCommandDepth >= 1) + `ASSERT_INIT(HostWriteBufferSizeValid, + HostWriteBufferBytes >= HostDataBytes && + (HostWriteBufferBytes % HostDataBytes) == 0) typedef logic [HostAddrWidth-1:0] host_addr_t; typedef logic [HostAddrWidth:0] host_ext_addr_t; typedef logic [ChipSelWidth-1:0] chip_sel_idx_t; - // Unpack the aggregate links at the midend boundary. + typedef struct packed { + hyper_cmd_t cmd; + hyperbus_pkg::hyper_host_size_t size; + logic [HostBusAddrWidth-1:0] start_addr; + hyperbus_pkg::hyper_blen_t beats; + logic write; + logic start_adapter; + } command_stage_t; + + ///////////////////////// + // Host stream buffers // + ///////////////////////// + + // Buffer the protocol-neutral host streams at the midend boundary. host_cmd_t host_cmd_i; logic host_req_valid_i; logic host_req_ready_o; @@ -68,32 +89,92 @@ module hyperbus_midend #( host_wrsp_t host_wrsp_o; logic host_wrsp_valid_o; logic host_wrsp_ready_i; - hyper_rx_t rx_i; - logic rx_valid_i; - logic rx_ready_o; - hyper_tx_t tx_o; - logic tx_valid_o; - logic tx_ready_i; - logic wrsp_error_i; - logic wrsp_valid_i; - logic wrsp_ready_o; - hyper_cmd_t cmd_o; - logic cmd_valid_o; - logic cmd_ready_i; - - assign host_cmd_i = host_link_req_i.cmd; - assign host_req_valid_i = host_link_req_i.cmd_valid; - assign host_w_i = host_link_req_i.w; - assign host_w_valid_i = host_link_req_i.w_valid; - assign host_r_ready_i = host_link_req_i.r_ready; - assign host_wrsp_ready_i = host_link_req_i.wrsp_ready; - - assign host_link_rsp_o.cmd_ready = host_req_ready_o; - assign host_link_rsp_o.w_ready = host_w_ready_o; - assign host_link_rsp_o.r = host_r_o; - assign host_link_rsp_o.r_valid = host_r_valid_o; - assign host_link_rsp_o.wrsp = host_wrsp_o; - assign host_link_rsp_o.wrsp_valid = host_wrsp_valid_o; + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( HostCommandDepth ), + .T ( host_cmd_t ) + ) i_host_cmd_fifo ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( ), + .data_i ( host_link_req_i.cmd ), + .valid_i ( host_link_req_i.cmd_valid ), + .ready_o ( host_link_rsp_o.cmd_ready ), + .data_o ( host_cmd_i ), + .valid_o ( host_req_valid_i ), + .ready_i ( host_req_ready_o ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( WriteFifoDepth ), + .T ( host_w_t ) + ) i_host_w_fifo ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( ), + .data_i ( host_link_req_i.w ), + .valid_i ( host_link_req_i.w_valid ), + .ready_o ( host_link_rsp_o.w_ready ), + .data_o ( host_w_i ), + .valid_o ( host_w_valid_i ), + .ready_i ( host_w_ready_o ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( ReadFifoDepth ), + .T ( host_r_t ) + ) i_host_r_fifo ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( ), + .data_i ( host_r_o ), + .valid_i ( host_r_valid_o ), + .ready_o ( host_r_ready_i ), + .data_o ( host_link_rsp_o.r ), + .valid_o ( host_link_rsp_o.r_valid ), + .ready_i ( host_link_req_i.r_ready ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( WriteRspFifoDepth ), + .T ( host_wrsp_t ) + ) i_host_wrsp_fifo ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .usage_o ( ), + .data_i ( host_wrsp_o ), + .valid_i ( host_wrsp_valid_o ), + .ready_o ( host_wrsp_ready_i ), + .data_o ( host_link_rsp_o.wrsp ), + .valid_o ( host_link_rsp_o.wrsp_valid ), + .ready_i ( host_link_req_i.wrsp_ready ) + ); + + ////////////////// + // Backend link // + ////////////////// + + hyper_rx_t rx_i; + hyper_tx_t tx_o; + hyper_cmd_t cmd_o; + logic rx_valid_i, rx_ready_o; + logic tx_valid_o, tx_ready_i; + logic wrsp_error_i, wrsp_valid_i, wrsp_ready_o; + logic cmd_valid_o, cmd_ready_i; + command_stage_t command_stage_in, command_stage_out; + logic command_stage_ready, command_stage_valid; assign rx_i = hyper_link_rsp_i.rx; assign rx_valid_i = hyper_link_rsp_i.rx_valid; @@ -106,86 +187,113 @@ module hyperbus_midend #( assign hyper_link_req_o.tx = tx_o; assign hyper_link_req_o.tx_valid = tx_valid_o; assign hyper_link_req_o.wrsp_ready = wrsp_ready_o; - assign hyper_link_req_o.cmd = cmd_o; - assign hyper_link_req_o.cmd_valid = cmd_valid_o; - - // Command decode and invalid-request response tracking. - typedef enum logic [1:0] { - ErrorIdle, - ErrorRead, - ErrorWrite, - ErrorWriteResp - } error_state_e; - - chip_sel_idx_t cmd_chip_sel_idx; - chip_sel_idx_t cmd_end_chip_sel_idx; - logic command_accepted; - logic adapter_started; - logic cmd_dec_valid; - logic cmd_dec_error; - logic cmd_end_dec_valid; - logic req_range_valid; - logic req_decode_error; - logic error_req_accepted; - error_state_e error_state_d, error_state_q; - hyperbus_pkg::hyper_blen_t error_beats_d, error_beats_q; - - host_r_t converted_host_r; - logic converted_host_r_valid; - logic converted_host_r_ready; - logic converted_host_w_ready; - host_w_t converted_host_w; - logic converted_host_w_valid; - - // Address decode and command segmentation. - host_cmd_t cmd_req; - host_addr_t req_phy_first_addr; - host_ext_addr_t req_phy_first_ext; - host_ext_addr_t req_phy_end_addr; - host_ext_addr_t req_last_addr; - host_ext_addr_t req_phy_bytes; - hyperbus_pkg::hyper_blen_t req_phy_burst; - host_ext_addr_t cmd_rule_end_addr; - logic req_addr_overflow; - logic atomic_range_valid; + assign hyper_link_req_o.cmd = command_stage_out.cmd; + assign hyper_link_req_o.cmd_valid = command_stage_valid; + + /////////////////////// + // Request selection // + /////////////////////// + + host_cmd_t cmd_req; + logic normal_req; + logic host_req_accepted; + logic command_accepted; + logic backend_command_accepted; + logic adapter_started; + logic normal_cmd_accepted; + logic normal_cmd_valid; + logic normal_req_ready; + + //////////////////// + // Address decode // + //////////////////// + + chip_sel_idx_t cmd_chip_sel_idx; + chip_sel_idx_t cmd_end_chip_sel_idx; + host_addr_t req_phy_first_addr; + host_addr_t req_phy_last_addr; + host_ext_addr_t req_phy_first_ext; + host_ext_addr_t req_phy_end_addr; + host_ext_addr_t req_last_addr; + host_ext_addr_t req_phy_bytes; + host_ext_addr_t cmd_rule_end_addr; + hyperbus_pkg::hyper_blen_t req_phy_burst; + logic cmd_dec_valid; + logic cmd_end_dec_valid; + logic req_addr_overflow; + logic req_range_valid; + logic req_decode_error; + logic atomic_range_valid; + + ////////////////////////// + // Command segmentation // + ////////////////////////// host_cmd_t segment_req_d, segment_req_q; hyperbus_pkg::hyper_blen_t segment_remaining_d, segment_remaining_q; hyperbus_pkg::hyper_blen_t cmd_remaining; - host_ext_addr_t cmd_rule_capacity; hyperbus_pkg::hyper_blen_t cmd_segment_burst; + host_ext_addr_t cmd_rule_capacity; host_addr_t cmd_next_addr; logic segment_pending_d, segment_pending_q; logic segment_final_d, segment_final_q; logic segment_wrsp_error_d, segment_wrsp_error_q; logic cmd_segment_final; - logic normal_cmd_accepted; logic normal_segment_complete; - // Atomic requests use the same command and data adapters as normal traffic. - logic atomic_req_accepted; - logic atomic_cmd_valid; - logic atomic_active; - logic atomic_completed; - logic normal_req; - logic atomic_request_valid; - host_cmd_t atomic_cmd; - host_w_t atomic_host_w; - logic atomic_host_w_valid; - logic atomic_host_w_ready; - host_r_t atomic_host_r; - logic atomic_host_r_valid; - host_wrsp_t atomic_host_wrsp; - logic atomic_host_wrsp_valid; - logic atomic_read_ready; - logic atomic_wrsp_ready; - - logic adapter_rx_last; - - logic trans_active_d; - logic trans_active_q; - logic trans_active_set; - logic trans_active_reset; - logic host_req_accepted; + logic normal_read_segment_complete; + logic normal_write_segment_complete; + + ////////////////// + // Atomic path // + ////////////////// + + host_cmd_t atomic_cmd; + host_w_t atomic_host_w; + host_r_t atomic_host_r; + host_wrsp_t atomic_host_wrsp; + logic atomic_req_accepted; + logic atomic_request_error; + logic atomic_cmd_valid; + logic atomic_active; + logic atomic_completed; + logic atomic_request_valid; + logic atomic_host_w_valid, atomic_host_w_ready; + logic atomic_host_r_valid; + logic atomic_host_wrsp_valid; + logic atomic_read_ready, atomic_wrsp_ready; + + /////////////////// + // Data adapters // + /////////////////// + + host_r_t converted_host_r; + host_w_t converted_host_w; + logic converted_host_r_valid, converted_host_r_ready; + logic converted_host_w_valid, converted_host_w_ready; + logic adapter_rx_last; + + ///////////////////// + // Error responses // + ///////////////////// + + typedef enum logic [1:0] { + ErrorIdle, + ErrorRead, + ErrorWrite, + ErrorWriteResp + } error_state_e; + + error_state_e error_state_d, error_state_q; + hyperbus_pkg::hyper_blen_t error_beats_d, error_beats_q; + logic error_req_accepted; + + ////////////////////////// + // Transaction lifetime // + ////////////////////////// + + logic trans_active_d, trans_active_q; + logic trans_active_set, trans_active_reset; + logic normal_transaction_completed; assign normal_req = host_cmd_i.atomic_op == hyperbus_pkg::HyperAtomicNone; assign atomic_request_valid = @@ -196,23 +304,23 @@ module hyperbus_midend #( ((host_cmd_i.atomic_op != hyperbus_pkg::HyperAtomicCompare) || (host_cmd_i.size != '0)) && atomic_range_valid; - assign cmd_valid_o = atomic_cmd_valid || - segment_pending_q || - (host_req_valid_i && !trans_active_q && cmd_dec_valid && - req_range_valid && normal_req); - assign host_req_ready_o = !trans_active_q && - (normal_req ? - (req_decode_error || (cmd_dec_valid && cmd_ready_i)) : 1'b1); + assign normal_cmd_valid = host_req_valid_i && !trans_active_q && cmd_dec_valid && + req_range_valid && normal_req; + assign normal_req_ready = req_decode_error || (cmd_dec_valid && command_stage_ready); + assign cmd_valid_o = atomic_cmd_valid || segment_pending_q || normal_cmd_valid; + assign host_req_ready_o = !trans_active_q && (!normal_req || normal_req_ready); assign host_req_accepted = host_req_valid_i && host_req_ready_o; - assign command_accepted = cmd_valid_o && cmd_ready_i; + assign command_accepted = cmd_valid_o && command_stage_ready; + assign backend_command_accepted = command_stage_valid && cmd_ready_i; assign normal_cmd_accepted = command_accepted && !atomic_cmd_valid; - assign adapter_started = atomic_cmd_valid ? command_accepted : - (normal_cmd_accepted && !segment_pending_q); + assign adapter_started = backend_command_accepted && command_stage_out.start_adapter; assign error_req_accepted = host_req_accepted && req_decode_error; assign atomic_req_accepted = host_req_accepted && !normal_req; - assign decode_error_o = error_req_accepted || - (atomic_req_accepted && !atomic_range_valid); - assign trans_active_o = trans_active_q; + assign atomic_request_error = atomic_req_accepted && !atomic_request_valid; + assign decode_error_o = error_req_accepted || atomic_request_error; + assign trans_active_o = trans_active_q || host_req_valid_i || host_w_valid_i || + host_link_rsp_o.r_valid || host_link_rsp_o.wrsp_valid || + command_stage_valid; always_comb begin : proc_cmd_cs cmd_o.cs = '0; @@ -242,42 +350,56 @@ module hyperbus_midend #( req_phy_bytes = req_phy_bytes << NumPhys; req_phy_burst = hyperbus_pkg::hyper_blen_t'(req_phy_bytes >> 1); req_phy_end_addr = req_phy_first_ext + req_phy_bytes; + req_phy_last_addr = host_addr_t'(req_phy_end_addr - 1'b1); req_addr_overflow = (req_last_addr > (host_ext_addr_t'(1) << HostAddrWidth)) || (req_phy_end_addr > (host_ext_addr_t'(1) << HostAddrWidth)); end + addr_decode #( + .NoIndices ( hyperbus_pkg::HyperNumChips ), + .NoRules ( hyperbus_pkg::HyperNumChips ), + .addr_t ( host_addr_t ), + .rule_t ( rule_t ), + .idx_t ( chip_sel_idx_t ) + ) i_start_addr_decode ( + .addr_i ( req_phy_first_addr ), + .addr_map_i ( chip_rules_i ), + .idx_o ( cmd_chip_sel_idx ), + .dec_valid_o ( cmd_dec_valid ), + .dec_error_o ( ), + .en_default_idx_i ( 1'b0 ), + .default_idx_i ( '0 ) + ); + + addr_decode #( + .NoIndices ( hyperbus_pkg::HyperNumChips ), + .NoRules ( hyperbus_pkg::HyperNumChips ), + .addr_t ( host_addr_t ), + .rule_t ( rule_t ), + .idx_t ( chip_sel_idx_t ) + ) i_end_addr_decode ( + .addr_i ( req_phy_last_addr ), + .addr_map_i ( chip_rules_i ), + .idx_o ( cmd_end_chip_sel_idx ), + .dec_valid_o ( cmd_end_dec_valid ), + .dec_error_o ( ), + .en_default_idx_i ( 1'b0 ), + .default_idx_i ( '0 ) + ); + // Software keeps ranges ordered and non-overlapping; transactions may cross contiguous ranges. always_comb begin : proc_req_rule_range - cmd_chip_sel_idx = '0; - cmd_end_chip_sel_idx = '0; - cmd_dec_valid = 1'b0; - cmd_end_dec_valid = 1'b0; - cmd_rule_end_addr = req_phy_first_ext; - - for (int unsigned i = 0; i < NumChips; i++) begin - host_ext_addr_t rule_start; - host_ext_addr_t rule_end; - - rule_start = host_ext_addr_t'(chip_rules_i[i].start_addr); - rule_end = (chip_rules_i[i].end_addr == '0) ? - (host_ext_addr_t'(1) << HostAddrWidth) : - host_ext_addr_t'(chip_rules_i[i].end_addr); - if (!cmd_dec_valid && (req_phy_first_ext >= rule_start) && - (req_phy_first_ext < rule_end)) begin - cmd_chip_sel_idx = chip_sel_idx_t'(i); - cmd_dec_valid = 1'b1; - cmd_rule_end_addr = rule_end; - end - if (!cmd_end_dec_valid && ((req_phy_end_addr - 1'b1) >= rule_start) && - ((req_phy_end_addr - 1'b1) < rule_end)) begin - cmd_end_chip_sel_idx = chip_sel_idx_t'(i); - cmd_end_dec_valid = 1'b1; - end + cmd_rule_end_addr = req_phy_first_ext; + if (cmd_dec_valid) begin + cmd_rule_end_addr = (chip_rules_i[cmd_chip_sel_idx].end_addr == '0) ? + (host_ext_addr_t'(1) << HostAddrWidth) : + host_ext_addr_t'( + chip_rules_i[cmd_chip_sel_idx].end_addr); end req_range_valid = !req_addr_overflow && cmd_dec_valid && cmd_end_dec_valid && (cmd_end_chip_sel_idx >= cmd_chip_sel_idx); - for (int unsigned i = 0; i < NumChips - 1; i++) begin + for (int unsigned i = 0; i < hyperbus_pkg::HyperNumChips - 1; i++) begin if ((i >= cmd_chip_sel_idx) && (i < cmd_end_chip_sel_idx) && (chip_rules_i[i].end_addr != chip_rules_i[i+1].start_addr)) begin req_range_valid = 1'b0; @@ -285,25 +407,32 @@ module hyperbus_midend #( end end - assign cmd_dec_error = !cmd_dec_valid; assign atomic_range_valid = req_range_valid && (cmd_chip_sel_idx == cmd_end_chip_sel_idx); assign req_decode_error = normal_req && - (cmd_dec_error || + (!cmd_dec_valid || (!atomic_active && !segment_pending_q && !req_range_valid)); assign cmd_o.trans.write = cmd_req.write; assign cmd_o.trans.burst_type = 1'b1; // Wrapping HyperBus bursts are not supported. assign cmd_o.trans.address_space = frontend_cfg_i.address_space; - assign cmd_o.trans.address = (NumPhys == 2) ? - (frontend_cfg_i.dual_phy ? - ((req_phy_first_addr & - ((host_addr_t'(1) << frontend_cfg_i.address_mask_msb) - 1)) >> 2) : - (((req_phy_first_addr & - ((host_addr_t'(1) << frontend_cfg_i.address_mask_msb) - 1)) >> 2) << 1)) : - ((req_phy_first_addr & - ((host_addr_t'(1) << frontend_cfg_i.address_mask_msb) - 1)) >> 1); + host_addr_t cmd_phy_address; + host_addr_t masked_req_address; + + always_comb begin : proc_cmd_address + masked_req_address = req_phy_first_addr & + ((host_addr_t'(1) << frontend_cfg_i.address_mask_msb) - 1); + cmd_phy_address = masked_req_address >> 1; + if (NumPhys == 2) begin + cmd_phy_address = masked_req_address >> 2; + if (!frontend_cfg_i.dual_phy) begin + cmd_phy_address = cmd_phy_address << 1; + end + end + end + + assign cmd_o.trans.address = cmd_phy_address; always_comb begin : proc_cmd_segment cmd_remaining = segment_pending_q ? segment_remaining_q : req_phy_burst; @@ -325,6 +454,28 @@ module hyperbus_midend #( cmd_o.trans.burst = cmd_segment_burst; end + assign command_stage_in.cmd = cmd_o; + assign command_stage_in.size = cmd_req.size; + assign command_stage_in.start_addr = cmd_req.addr[HostBusAddrWidth-1:0]; + assign command_stage_in.beats = cmd_req.beats; + assign command_stage_in.write = cmd_req.write; + assign command_stage_in.start_adapter = atomic_cmd_valid || !segment_pending_q; + + stream_register #( + .T ( command_stage_t ) + ) i_command_stage ( + .clk_i, + .rst_ni, + .clr_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .valid_i ( cmd_valid_o ), + .ready_o ( command_stage_ready ), + .data_i ( command_stage_in ), + .valid_o ( command_stage_valid ), + .ready_i ( cmd_ready_i ), + .data_o ( command_stage_out ) + ); + assign adapter_rx_last = rx_i.last && (atomic_active || segment_final_q); hyperbus_read_adapter #( @@ -335,19 +486,19 @@ module hyperbus_midend #( ) i_read_adapter ( .clk_i, .rst_ni, - .size_i ( cmd_req.size ), - .start_i ( adapter_started && !cmd_req.write ), - .dual_phy_i ( frontend_cfg_i.dual_phy ), - .start_addr_i ( cmd_req.addr[HostBusAddrWidth-1:0] ), - .burst_len_i ( cmd_req.beats ), - .phy_valid_i ( rx_valid_i ), - .phy_ready_o ( rx_ready_o ), - .data_i ( rx_i.data ), - .last_i ( adapter_rx_last ), - .error_i ( rx_i.error ), - .host_valid_o ( converted_host_r_valid ), - .host_ready_i ( converted_host_r_ready ), - .data_o ( converted_host_r ) + .size_i ( command_stage_out.size ), + .start_i ( adapter_started && !command_stage_out.write ), + .dual_phy_i ( frontend_cfg_i.dual_phy ), + .start_addr_i ( command_stage_out.start_addr ), + .burst_len_i ( command_stage_out.beats ), + .phy_valid_i ( rx_valid_i ), + .phy_ready_o ( rx_ready_o ), + .data_i ( rx_i.data ), + .last_i ( adapter_rx_last ), + .error_i ( rx_i.error ), + .host_valid_o ( converted_host_r_valid ), + .host_ready_i ( converted_host_r_ready ), + .data_o ( converted_host_r ) ); hyperbus_write_adapter #( @@ -357,18 +508,18 @@ module hyperbus_midend #( ) i_write_adapter ( .clk_i, .rst_ni, - .size_i ( cmd_req.size ), - .start_i ( adapter_started && cmd_req.write ), - .dual_phy_i ( frontend_cfg_i.dual_phy ), - .start_addr_i ( cmd_req.addr[HostBusAddrWidth-1:0] ), - .data_i ( converted_host_w ), - .host_valid_i ( converted_host_w_valid ), - .host_ready_o ( converted_host_w_ready ), - .data_o ( tx_o.data ), - .last_o ( tx_o.last ), - .strb_o ( tx_o.strb ), - .phy_valid_o ( tx_valid_o ), - .phy_ready_i ( tx_ready_i ) + .size_i ( command_stage_out.size ), + .start_i ( adapter_started && command_stage_out.write ), + .dual_phy_i ( frontend_cfg_i.dual_phy ), + .start_addr_i ( command_stage_out.start_addr ), + .data_i ( converted_host_w ), + .host_valid_i ( converted_host_w_valid ), + .host_ready_o ( converted_host_w_ready ), + .data_o ( tx_o.data ), + .last_o ( tx_o.last ), + .strb_o ( tx_o.strb ), + .phy_valid_o ( tx_valid_o ), + .phy_ready_i ( tx_ready_i ) ); hyperbus_atomic_handler #( @@ -388,7 +539,7 @@ module hyperbus_midend #( .completed_o ( atomic_completed ), .command_o ( atomic_cmd ), .command_valid_o ( atomic_cmd_valid ), - .command_ready_i ( cmd_ready_i ), + .command_ready_i ( command_stage_ready ), .host_w_i ( host_w_i ), .host_w_valid_i ( host_w_valid_i ), .host_w_ready_o ( atomic_host_w_ready ), @@ -409,9 +560,12 @@ module hyperbus_midend #( .write_rsp_ready_o ( atomic_wrsp_ready ) ); + assign normal_read_segment_complete = !segment_req_q.write && + rx_valid_i && rx_ready_o && rx_i.last; + assign normal_write_segment_complete = segment_req_q.write && + wrsp_valid_i && wrsp_ready_o; assign normal_segment_complete = trans_active_q && !atomic_active && - (segment_req_q.write ? (wrsp_valid_i && wrsp_ready_o) : - (rx_valid_i && rx_ready_o && rx_i.last)); + (normal_read_segment_complete || normal_write_segment_complete); always_comb begin : proc_segments segment_req_d = segment_req_q; @@ -449,12 +603,14 @@ module hyperbus_midend #( error_beats_d = error_beats_q; unique case (error_state_q) + // Capture a rejected command and select its response stream. ErrorIdle: begin if (error_req_accepted) begin error_beats_d = host_cmd_i.beats; error_state_d = host_cmd_i.write ? ErrorWrite : ErrorRead; end end + // Return one decode-error beat per requested read beat. ErrorRead: begin if (host_r_valid_o && host_r_ready_i) begin error_beats_d = error_beats_q - 1'b1; @@ -463,11 +619,13 @@ module hyperbus_midend #( end end end + // Drain all write data belonging to a rejected write command. ErrorWrite: begin if (host_w_valid_i && host_w_ready_o && host_w_i.last) begin error_state_d = ErrorWriteResp; end end + // Return the final decode-error write response. ErrorWriteResp: begin if (host_wrsp_valid_o && host_wrsp_ready_i) begin error_state_d = ErrorIdle; @@ -526,9 +684,11 @@ module hyperbus_midend #( assign trans_active_set = (normal_cmd_accepted && !segment_pending_q) || error_req_accepted || atomic_req_accepted; - assign trans_active_reset = atomic_active ? atomic_completed : - ((host_r_valid_o && host_r_ready_i && host_r_o.last) || - (host_wrsp_valid_o && host_wrsp_ready_i)); + assign normal_transaction_completed = + (host_r_valid_o && host_r_ready_i && host_r_o.last) || + (host_wrsp_valid_o && host_wrsp_ready_i); + assign trans_active_reset = (atomic_active && atomic_completed) || + (!atomic_active && normal_transaction_completed); always_comb begin : proc_trans_active trans_active_d = trans_active_q; @@ -553,6 +713,7 @@ module hyperbus_midend #( (host_cmd_i.atomic_op <= hyperbus_pkg::HyperAtomicUnsignedMin)) `ASSERT(HostReqAtomicWrite, (host_req_accepted && (host_cmd_i.atomic_op != hyperbus_pkg::HyperAtomicNone)) |-> host_cmd_i.write) - `ASSERT(BackendBurstNonzero, command_accepted |-> (cmd_o.trans.burst != '0)) + `ASSERT(BackendBurstNonzero, backend_command_accepted |-> + (command_stage_out.cmd.trans.burst != '0)) endmodule diff --git a/src/hyperbus_pkg.sv b/src/hyperbus_pkg.sv index fc85bce..589a57a 100644 --- a/src/hyperbus_pkg.sv +++ b/src/hyperbus_pkg.sv @@ -4,11 +4,18 @@ package hyperbus_pkg; + // The register map and physical interface expose every supported chip select. + localparam int unsigned HyperNumChips = 8; + + //////////////////////////// + // Host-side transaction // + //////////////////////////// + // Maximal burst size: 2^8 1024-bit words as 16-bit words (plus one as not decremented) localparam unsigned HyperBurstWidth = 8 + $clog2(1024/16) + 1; typedef logic [HyperBurstWidth-1:0] hyper_blen_t; - typedef logic [2:0] hyper_host_size_t; + typedef logic [2:0] hyper_host_size_t; typedef enum logic [1:0] { HyperBurstIncr, @@ -21,6 +28,7 @@ package hyperbus_pkg; HyperAtomicSwap, HyperAtomicCompare, HyperAtomicAdd, + HyperAtomicAnd, HyperAtomicClear, HyperAtomicXor, HyperAtomicSet, @@ -37,7 +45,10 @@ package hyperbus_pkg; HyperRespAtomicError } hyper_resp_e; - // configuration type + /////////////////// + // Configuration // + /////////////////// + typedef struct packed { logic [3:0] t_latency_access; logic en_latency_additional; @@ -61,6 +72,10 @@ package hyperbus_pkg; logic dual_phy; } phy_cfg_t; + ////////////////////////// + // Backend transaction // + ////////////////////////// + typedef struct packed { logic write; // transaction is a write hyper_blen_t burst; @@ -70,12 +85,12 @@ package hyperbus_pkg; } hyper_tf_t; typedef struct packed { - logic [15:0] data; - logic last; - logic error; + logic [15:0] data; + logic last; + logic error; } phy_rx_t; - typedef enum logic[3:0] { + typedef enum logic [3:0] { Startup, Idle, DelayCK, diff --git a/src/hyperbus_read_adapter.sv b/src/hyperbus_read_adapter.sv index 658c6e8..3e7aac5 100644 --- a/src/hyperbus_read_adapter.sv +++ b/src/hyperbus_read_adapter.sv @@ -36,25 +36,36 @@ module hyperbus_read_adapter #( localparam int unsigned WordCntWidth = (PhyBeatsPerHost == 1) ? 1 : $clog2(PhyBeatsPerHost); - typedef enum logic [2:0] { + typedef enum logic [1:0] { Idle, - WaitData, - Sample, - CntReady + WaitPhy, + CollectPhy, + EmitHost } read_adapter_state_e; + ////////////////////// + // Persistent state // + ////////////////////// + read_adapter_state_e state_d, state_q; logic [BurstLength-1:0] byte_host_addr_d, byte_host_addr_q; logic [BurstLength-1:0] byte_phy_cnt_d, byte_phy_cnt_q; logic [BurstLength-1:0] last_addr_d, last_addr_q; logic [3:0] size_d, size_q; T data_buffer_d, data_buffer_q; + + ////////////////////// + // Address tracking // + ////////////////////// + logic [WordCntWidth-1:0] word_cnt; logic enough_data; logic enough_data_q; logic sent_available_data; logic [BurstLength-1:0] next_host_addr; logic host_last; + logic host_accepted; + logic converted_accepted; logic [16*NumPhys-1:0] converted_data; logic converted_last; logic converted_error; @@ -68,6 +79,12 @@ module hyperbus_read_adapter #( assign enough_data_q = byte_phy_cnt_q >= next_host_addr; assign sent_available_data = byte_host_addr_d >= byte_phy_cnt_q; assign host_last = data_buffer_q.last && (last_addr_q == next_host_addr); + assign host_accepted = host_valid_o && host_ready_i; + assign converted_accepted = converted_valid && converted_ready; + + ////////////////////// + // Host read output // + ////////////////////// assign data_o.data = data_buffer_q.data; assign data_o.resp = data_buffer_q.resp; @@ -88,26 +105,30 @@ module hyperbus_read_adapter #( last_addr_d = ((start_addr_i >> size_i) << size_i) + (burst_len_i << size_i); size_d = size_i; end - if (host_valid_o && host_ready_i) begin + if (host_accepted) begin byte_host_addr_d = ((byte_host_addr_q >> size_q) << size_q) + (1 << size_q); end - if (converted_valid && converted_ready) begin + if (converted_accepted) begin byte_phy_cnt_d = byte_phy_cnt_q + NumPhys * 2; end end + ///////////////////// + // Beat assembly // + ///////////////////// + always_comb begin : proc_sample data_buffer_d = data_buffer_q; - if (state_d == Idle) begin + if (state_q == Idle) begin data_buffer_d.last = 1'b0; data_buffer_d.data = '0; data_buffer_d.resp = hyperbus_pkg::HyperRespOkay; end else begin - if (host_valid_o && host_ready_i) begin + if (host_accepted) begin data_buffer_d.resp = hyperbus_pkg::HyperRespOkay; end - if (converted_ready && converted_valid) begin + if (converted_accepted) begin data_buffer_d.data[word_cnt*(16*NumPhys) +: (16*NumPhys)] = converted_data; if (converted_error) begin data_buffer_d.resp = hyperbus_pkg::HyperRespAccessError; @@ -117,38 +138,46 @@ module hyperbus_read_adapter #( end end + /////////////////////////// + // Adapter state machine // + /////////////////////////// + always_comb begin : proc_fsm state_d = state_q; host_valid_o = 1'b0; converted_ready = 1'b0; unique case (state_q) + // Wait for a command to initialize address tracking. Idle: begin if (start_i) begin - state_d = WaitData; + state_d = WaitPhy; end end - WaitData: begin + // Accept the first physical word of the host beat. + WaitPhy: begin converted_ready = 1'b1; if (converted_valid) begin - state_d = Sample; + state_d = CollectPhy; end end - Sample: begin + // Accumulate physical words until one complete host beat is available. + CollectPhy: begin converted_ready = 1'b1; if (enough_data) begin - state_d = CntReady; + state_d = EmitHost; end end - CntReady: begin + // Hold the assembled host beat until accepted. + EmitHost: begin host_valid_o = enough_data_q; converted_ready = !enough_data_q; - if (host_valid_o && host_ready_i) begin + if (host_accepted) begin if (data_o.last || (last_addr_q == byte_host_addr_d)) begin state_d = Idle; end else if (sent_available_data) begin converted_ready = 1'b1; - state_d = converted_valid ? Sample : WaitData; + state_d = converted_valid ? CollectPhy : WaitPhy; end end end @@ -158,6 +187,10 @@ module hyperbus_read_adapter #( endcase end + ////////////////////////// + // Physical-width adapter // + ////////////////////////// + if (NumPhys == 2) begin : gen_dual_phy logic [15:0] lower_data_d, lower_data_q; logic lower_error_d, lower_error_q; @@ -207,6 +240,10 @@ module hyperbus_read_adapter #( end end + ///////////////////// + // State registers // + ///////////////////// + `FFARN(state_q, state_d, Idle, clk_i, rst_ni) `FFARN(data_buffer_q, data_buffer_d, '0, clk_i, rst_ni) `FFARN(byte_host_addr_q, byte_host_addr_d, '0, clk_i, rst_ni) diff --git a/src/hyperbus_synchronous.sv b/src/hyperbus_synchronous.sv index f6b48be..bad36a5 100644 --- a/src/hyperbus_synchronous.sv +++ b/src/hyperbus_synchronous.sv @@ -6,20 +6,21 @@ `include "common_cells/assertions.svh" module hyperbus_synchronous #( - parameter int unsigned NumChips = -1, - parameter int unsigned NumPhys = 2, - parameter int unsigned AxiAddrWidth = -1, - parameter int unsigned AxiDataWidth = -1, - parameter int unsigned AxiIdWidth = -1, - parameter int unsigned AxiUserWidth = -1, - parameter type axi_req_t = logic, - parameter type axi_rsp_t = logic, - parameter int unsigned RegDataWidth = -1, - parameter type reg_req_t = logic, - parameter type reg_rsp_t = logic, - parameter type axi_rule_t = logic, - parameter int unsigned PhyStartupCycles = 300 * 200, - parameter int unsigned SyncStages = 2 + parameter int unsigned NumPhys = 2, + parameter int unsigned AxiAddrWidth = -1, + parameter int unsigned AxiDataWidth = -1, + parameter int unsigned AxiIdWidth = -1, + parameter int unsigned AxiUserWidth = -1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter int unsigned RegDataWidth = -1, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type axi_rule_t = logic, + parameter int unsigned HostCommandDepth = 8, + parameter int unsigned HostWriteBufferBytes = 128, + parameter int unsigned PhyStartupCycles = 300 * 200, + parameter int unsigned SyncStages = 2 ) ( input logic clk_sys_i, input logic rst_sys_ni, @@ -34,7 +35,7 @@ module hyperbus_synchronous #( input reg_req_t reg_req_i, output reg_rsp_t reg_rsp_o, - output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, + output logic [NumPhys-1:0][hyperbus_pkg::HyperNumChips-1:0] hyper_cs_no, output logic [NumPhys-1:0] hyper_ck_o, output logic [NumPhys-1:0] hyper_ck_no, output logic [NumPhys-1:0] hyper_rwds_o, @@ -52,10 +53,19 @@ module hyperbus_synchronous #( typedef logic [AxiDataWidth-1:0] host_data_t; typedef logic [AxiDataWidth/8-1:0] host_strb_t; `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) - `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys, NumChips) + `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys) + + ///////////////////// + // Clock and reset // + ///////////////////// + + logic clk_backend; + logic rst_backend_n; + + //////////////////////// + // Configuration path // + //////////////////////// - logic clk_backend; - logic rst_backend_n; hyperbus_pkg::phy_cfg_t frontend_cfg_apply; logic frontend_cfg_apply_valid; logic frontend_cfg_apply_ready; @@ -68,7 +78,11 @@ module hyperbus_synchronous #( logic backend_cfg_apply_ready; hyperbus_pkg::frontend_cfg_t frontend_cfg; - axi_rule_t [NumChips-1:0] frontend_chip_rules; + axi_rule_t [hyperbus_pkg::HyperNumChips-1:0] frontend_chip_rules; + + //////////////////// + // Dataflow links // + //////////////////// host_req_t host_req; host_rsp_t host_rsp; @@ -90,8 +104,11 @@ module hyperbus_synchronous #( assign backend_req = midend_req; assign midend_rsp = backend_rsp; + //////////////////////////// + // Configuration frontend // + //////////////////////////// + hyperbus_cfg_frontend #( - .NumChips ( NumChips ), .NumPhys ( NumPhys ), .reg_req_t ( reg_req_t ), .reg_rsp_t ( reg_rsp_t ), @@ -118,6 +135,10 @@ module hyperbus_synchronous #( .decode_error_i ( midend_decode_error ) ); + ////////////////// + // AXI frontend // + ////////////////// + hyperbus_axi_frontend #( .AxiDataWidth ( AxiDataWidth ), .AxiAddrWidth ( AxiAddrWidth ), @@ -138,23 +159,28 @@ module hyperbus_synchronous #( .host_rsp_i ( host_rsp ) ); + //////////// + // Midend // + //////////// + hyperbus_midend #( - .HostAddrWidth ( AxiAddrWidth ), - .HostDataWidth ( AxiDataWidth ), - .NumChips ( NumChips ), - .NumPhys ( NumPhys ), - .host_cmd_t ( host_cmd_t ), - .host_w_t ( host_w_t ), - .host_r_t ( host_r_t ), - .host_wrsp_t ( host_wrsp_t ), - .host_req_t ( host_req_t ), - .host_rsp_t ( host_rsp_t ), - .hyper_rx_t ( hyper_rx_t ), - .hyper_tx_t ( hyper_tx_t ), - .hyper_cmd_t ( hyper_cmd_t ), - .hyper_req_t ( hyper_req_t ), - .hyper_rsp_t ( hyper_rsp_t ), - .rule_t ( axi_rule_t ) + .HostAddrWidth ( AxiAddrWidth ), + .HostDataWidth ( AxiDataWidth ), + .NumPhys ( NumPhys ), + .HostCommandDepth ( HostCommandDepth ), + .HostWriteBufferBytes ( HostWriteBufferBytes ), + .host_cmd_t ( host_cmd_t ), + .host_w_t ( host_w_t ), + .host_r_t ( host_r_t ), + .host_wrsp_t ( host_wrsp_t ), + .host_req_t ( host_req_t ), + .host_rsp_t ( host_rsp_t ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .hyper_cmd_t ( hyper_cmd_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ), + .rule_t ( axi_rule_t ) ) i_midend ( .clk_i ( clk_sys_i ), .rst_ni ( rst_sys_ni ), @@ -168,8 +194,11 @@ module hyperbus_synchronous #( .hyper_link_rsp_i ( midend_rsp ) ); + ///////////// + // Backend // + ///////////// + hyperbus_backend #( - .NumChips ( NumChips ), .NumPhys ( NumPhys ), .StartupCycles ( PhyStartupCycles ), .SyncStages ( SyncStages ), diff --git a/src/hyperbus_write_adapter.sv b/src/hyperbus_write_adapter.sv index 65b9fc3..a63a2c9 100644 --- a/src/hyperbus_write_adapter.sv +++ b/src/hyperbus_write_adapter.sv @@ -36,10 +36,10 @@ module hyperbus_write_adapter #( localparam int unsigned WordCntWidth = (PhyBeatsPerHost == 1) ? 1 : $clog2(PhyBeatsPerHost); - typedef enum logic [2:0] { + typedef enum logic [1:0] { Idle, - Sample, - CntReady + CollectHost, + EmitPhy } write_adapter_state_e; typedef struct packed { @@ -48,33 +48,77 @@ module hyperbus_write_adapter #( logic last; } write_buffer_t; + ////////////////////// + // Persistent state // + ////////////////////// + write_adapter_state_e state_d, state_q; write_buffer_t data_buffer_d, data_buffer_q; - logic upsize; - logic enough_data; logic first_tx_d, first_tx_q; logic [NumPhys*2-1:0] mask_strobe_d, mask_strobe_q; - logic [WordCntWidth-1:0] word_cnt; logic [AddrWidth-1:0] byte_idx_d, byte_idx_q; logic [3:0] size_d, size_q; logic [AddrWidth-1:0] cnt_data_phy_d, cnt_data_phy_q; + + ////////////////////// + // Address tracking // + ////////////////////// + + logic upsize; + logic enough_data; + logic [WordCntWidth-1:0] word_cnt; + logic [AddrWidth-1:0] cnt_data_phy_next; logic keep_sending; logic [16*NumPhys-1:0] converted_data; logic [2*NumPhys-1:0] converted_strb; logic converted_last; logic converted_valid; logic converted_ready; + logic host_accepted; + logic converted_accepted; assign upsize = ((size_q == 1) && (NumPhys == 2)) || (size_q == 0); assign enough_data = !upsize; - assign keep_sending = (size_d > ($clog2(NumPhys) + 1)) && - (cnt_data_phy_d != byte_idx_q); + assign cnt_data_phy_next = cnt_data_phy_q + NumPhys * 2; + // Determine whether another PHY word follows without depending on downstream readiness. + assign keep_sending = (size_q > ($clog2(NumPhys) + 1)) && + (cnt_data_phy_next != byte_idx_q); assign word_cnt = cnt_data_phy_q >> ($clog2(NumPhys) + 1); assign converted_data = data_buffer_q.data[(16*NumPhys)*word_cnt +: (16*NumPhys)]; assign converted_strb = data_buffer_q.strb[(2*NumPhys)*word_cnt +: (2*NumPhys)] & mask_strobe_q; assign converted_last = data_buffer_q.last && (!keep_sending || upsize); + assign host_accepted = host_valid_i && host_ready_o; + assign converted_accepted = converted_valid && converted_ready; + + //////////////////////// + // Control conditions // + //////////////////////// + + logic collect_complete; + logic collect_partial_last; + logic phy_last_accepted; + logic wide_host_word_emitted; + logic wide_host_word_can_emit; + logic wide_host_word_needs_collect; + logic wide_host_word_without_data; + logic narrow_group_emitted; + assign collect_complete = host_accepted && + (enough_data || (byte_idx_d[NumPhys-1:0] == '0) || data_i.last); + assign collect_partial_last = host_accepted && !enough_data && + (byte_idx_d[NumPhys-1:0] != '0) && data_i.last; + + assign phy_last_accepted = converted_accepted && converted_last; + assign wide_host_word_emitted = converted_accepted && !converted_last && + (size_d >= NumPhys) && (cnt_data_phy_d == byte_idx_q); + assign wide_host_word_can_emit = wide_host_word_emitted && host_valid_i && enough_data; + assign wide_host_word_needs_collect = wide_host_word_emitted && + host_valid_i && !enough_data; + assign wide_host_word_without_data = wide_host_word_emitted && !host_valid_i; + assign narrow_group_emitted = converted_accepted && !converted_last && + (size_d < NumPhys) && + (cnt_data_phy_d[NumPhys-1:0] == '0); always_comb begin : proc_counters byte_idx_d = byte_idx_q; @@ -88,22 +132,25 @@ module hyperbus_write_adapter #( cnt_data_phy_d = (start_addr_i >> NumPhys) << NumPhys; first_tx_d = 1'b1; end - if (host_valid_i && host_ready_o) begin + if (host_accepted) begin byte_idx_d = ((byte_idx_q >> size_d) << size_d) + (1 << size_d); first_tx_d = 1'b0; end - if (converted_valid && converted_ready) begin + if (converted_accepted) begin cnt_data_phy_d = cnt_data_phy_q + NumPhys * 2; end end + /////////////////// + // Beat assembly // + /////////////////// + always_comb begin : proc_sample data_buffer_d = data_buffer_q; - if (state_d == Idle) begin + if (state_q == Idle) begin data_buffer_d.last = 1'b0; - data_buffer_d.data = '0; - end else if (host_ready_o && host_valid_i) begin + end else if (host_accepted) begin if (!upsize) begin // A full host beat remains buffered until all PHY words are emitted. data_buffer_d.data = data_i.data; @@ -129,54 +176,61 @@ module hyperbus_write_adapter #( end end + // Mask bytes beyond a short final host beat. + always_comb begin : proc_mask_strobe + mask_strobe_d = mask_strobe_q; + + if (state_q == Idle) begin + mask_strobe_d = '1; + end + if (collect_partial_last) begin + for (int unsigned i = 0; i < NumPhys * 2; i++) begin + mask_strobe_d[i] = i < byte_idx_d[NumPhys-1:0]; + end + end + end + + /////////////////////////// + // Adapter state machine // + /////////////////////////// + always_comb begin : proc_fsm - state_d = state_q; - mask_strobe_d = mask_strobe_q; - host_ready_o = 1'b0; + state_d = state_q; + host_ready_o = 1'b0; converted_valid = 1'b0; unique case (state_q) + // Wait for a command to initialize address tracking. Idle: begin - mask_strobe_d = '1; if (start_i) begin - state_d = Sample; + state_d = CollectHost; end end - Sample: begin + // Gather enough host bytes to form the next physical word. + CollectHost: begin host_ready_o = 1'b1; - if (host_valid_i && enough_data) begin - state_d = CntReady; - end else if (host_valid_i) begin - if (byte_idx_d[NumPhys-1:0] != '0) begin - if (data_i.last) begin - state_d = CntReady; - for (int unsigned i = 0; i < NumPhys * 2; i++) begin - mask_strobe_d[i] = i < byte_idx_d[NumPhys-1:0]; - end - end - end else begin - state_d = CntReady; - end + if (collect_complete) begin + state_d = EmitPhy; end end - CntReady: begin + // Emit physical words and return for more host data when needed. + EmitPhy: begin converted_valid = 1'b1; - if (converted_ready) begin - if (converted_last) begin - state_d = start_i ? Sample : Idle; - end else if (size_d >= NumPhys) begin - if (cnt_data_phy_d != byte_idx_q) begin - state_d = CntReady; - end else if (host_valid_i) begin - host_ready_o = 1'b1; - state_d = enough_data ? CntReady : Sample; - end else begin - state_d = Sample; - end - end else if (cnt_data_phy_d[NumPhys-1:0] == '0) begin - host_ready_o = !upsize; - state_d = Sample; - end + if (phy_last_accepted && start_i) begin + state_d = CollectHost; + end else if (phy_last_accepted) begin + state_d = Idle; + end else if (wide_host_word_can_emit) begin + host_ready_o = 1'b1; + state_d = EmitPhy; + end else if (wide_host_word_needs_collect) begin + host_ready_o = 1'b1; + state_d = CollectHost; + end else if (wide_host_word_without_data) begin + state_d = CollectHost; + end else if (narrow_group_emitted) begin + host_ready_o = !upsize; + state_d = CollectHost; end end default: begin @@ -185,6 +239,10 @@ module hyperbus_write_adapter #( endcase end + ////////////////////////// + // Physical-width adapter // + ////////////////////////// + if (NumPhys == 2) begin : gen_dual_phy logic split_d, split_q; @@ -225,6 +283,10 @@ module hyperbus_write_adapter #( end end + ///////////////////// + // State registers // + ///////////////////// + `FFARN(state_q, state_d, Idle, clk_i, rst_ni) `FFARN(data_buffer_q, data_buffer_d, '0, clk_i, rst_ni) `FFARN(byte_idx_q, byte_idx_d, '0, clk_i, rst_ni) diff --git a/test/axi_hyper_tb.sv b/test/axi_hyper_tb.sv index c3cf434..a6b83aa 100644 --- a/test/axi_hyper_tb.sv +++ b/test/axi_hyper_tb.sv @@ -7,7 +7,7 @@ module axi_hyper_tb import axi_pkg::*; #( - parameter int unsigned NumChips = 2, + parameter int unsigned NumConnectedChips = 2, parameter int unsigned NumPhys = 2, parameter int unsigned IsClockODelayed = 0, parameter int unsigned NB_CH = 1, @@ -759,7 +759,7 @@ module axi_hyper_tb logic [31:0] segment_start_snapshot; logic reg_error; - if (NumChips < 2) begin + if (NumConnectedChips < 2) begin return; end @@ -818,7 +818,7 @@ module axi_hyper_tb axi_ctrl_master_t::r_beat_t r; logic reg_error; - if (NumChips < 2) begin + if (NumConnectedChips < 2) begin return; end @@ -948,7 +948,7 @@ module axi_hyper_tb logic [31:0] segment_start_snapshot; logic reg_error; - if (NumChips < 2) begin + if (NumConnectedChips < 2) begin return; end @@ -1035,7 +1035,7 @@ module axi_hyper_tb mst_scoreboard.monitor(); // Map each chip to a distinct 16 MiB host-address window. - if (NumChips > 1) begin + if (NumConnectedChips > 1) begin reg_master.send_write(32'h3c, 32'h8200_0000, '1, s_reg_error); if (s_reg_error != 1'b0) $error("unexpected error"); reg_master.send_write(32'h38, 32'h8100_0000, '1, s_reg_error); @@ -1172,7 +1172,7 @@ module axi_hyper_tb .RegAw ( RegBusAW ), .RegDw ( RegBusDW ), - .NumChips ( NumChips ), + .NumConnectedChips ( NumConnectedChips ), .NumPhys ( NumPhys ), .AnnotateSdf ( TbAnnotateSdf ), .IsClockODelayed ( IsClockODelayed ), diff --git a/test/dut_if.sv b/test/dut_if.sv index 5075bf1..d7485e8 100644 --- a/test/dut_if.sv +++ b/test/dut_if.sv @@ -22,7 +22,7 @@ module dut_if parameter int RegAw = -1, parameter int RegDw = -1, - parameter int NumChips = -1, + parameter int NumConnectedChips = 2, parameter int NumPhys = -1, parameter bit AnnotateSdf = 1'b1, parameter int IsClockODelayed = -1, @@ -74,7 +74,7 @@ module dut_if `REG_BUS_ASSIGN_FROM_RSP(reg_slv_if,reg_resp) - logic [NumPhys-1:0][NumChips-1:0] hyper_cs_n_wire; + logic [NumPhys-1:0][hyperbus_pkg::HyperNumChips-1:0] hyper_cs_n_wire; logic [NumPhys-1:0] hyper_ck_wire; logic [NumPhys-1:0] hyper_ck_n_wire; logic [NumPhys-1:0] hyper_rwds_o; @@ -85,10 +85,10 @@ module dut_if logic [NumPhys-1:0] hyper_dq_oe; logic [NumPhys-1:0] hyper_reset_n_wire; logic phy_clk; - logic [NumPhys-1:0][NumChips-1:0] hyper_cs_n_q; + logic [NumPhys-1:0][hyperbus_pkg::HyperNumChips-1:0] hyper_cs_n_q; logic segment_start; - wire [NumPhys-1:0][NumChips-1:0] pad_hyper_csn; + wire [NumPhys-1:0][NumConnectedChips-1:0] pad_hyper_csn; wire [NumPhys-1:0] pad_hyper_ck; wire [NumPhys-1:0] pad_hyper_ckn; wire [NumPhys-1:0] pad_hyper_rwds; @@ -154,7 +154,6 @@ module dut_if // DUT hyperbus_test_dut #( .DutVariant ( DutVariant ), - .NumChips ( NumChips ), .NumPhys ( NumPhys ), .AxiAddrWidth ( AxiAddrWidth ), .AxiDataWidth ( AxiDataWidth ), @@ -196,7 +195,7 @@ module dut_if generate for (genvar i=0; i Date: Thu, 23 Jul 2026 17:48:54 +0200 Subject: [PATCH 11/11] Expand HyperBus protocol and performance regression Add focused atomic-handler verification and exercise host buffering, sticky atomic errors, isochronous divider backpressure, transfer segmentation, topology variants, and maximum-burst throughput. --- Bender.yml | 1 + scripts/start.tcl | 1 + test/axi_hyper_tb.sv | 118 +++++++++- test/hyperbus_atomic_handler_tb.sv | 361 +++++++++++++++++++++++++++++ 4 files changed, 479 insertions(+), 2 deletions(-) create mode 100644 test/hyperbus_atomic_handler_tb.sv diff --git a/Bender.yml b/Bender.yml index 4842667..df9a1bc 100644 --- a/Bender.yml +++ b/Bender.yml @@ -77,4 +77,5 @@ sources: - test/dut_if.sv - test/hyperbus_tb_pkg.sv - test/axi_hyper_tb.sv + - test/hyperbus_atomic_handler_tb.sv - test/hyperbus_cfg_regs_tb.sv diff --git a/scripts/start.tcl b/scripts/start.tcl index e1256cc..b192465 100644 --- a/scripts/start.tcl +++ b/scripts/start.tcl @@ -41,6 +41,7 @@ run_test axi_hyper_tb_isochronous sim_run_isochronous.wlf run_test axi_hyper_tb_synchronous sim_run_synchronous.wlf run_test axi_hyper_tb_asynchronous sim_run_asynchronous.wlf run_test axi_hyper_tb_synchronous_one_phy sim_run_synchronous_one_phy.wlf +run_test hyperbus_atomic_handler_tb sim_run_atomic_handler.wlf run_test hyperbus_cfg_regs_tb sim_run_cfg_regs.wlf quit -code $regression_failed -f diff --git a/test/axi_hyper_tb.sv b/test/axi_hyper_tb.sv index a6b83aa..5bb42d2 100644 --- a/test/axi_hyper_tb.sv +++ b/test/axi_hyper_tb.sv @@ -505,6 +505,78 @@ module axi_hyper_tb end endtask + task automatic check_midend_buffering(input axi_ctrl_master_t axi_drv); + localparam int unsigned NumTransactions = 4; + localparam int unsigned WriteBeats = 4; + localparam axi_addr_t BufferBaseAddr = axi_addr_t'(32'h8000_2000); + axi_ctrl_master_t::ax_beat_t ax; + axi_ctrl_master_t::w_beat_t w; + axi_ctrl_master_t::b_beat_t b; + axi_ctrl_master_t::r_beat_t r; + axi_addr_t transaction_addr; + + $display("==========================="); + $display("= Midend queue buffering ="); + $display("==========================="); + + // Fill all write contexts and the 128-byte W buffer before accepting responses. + for (int unsigned transaction = 0; transaction < NumTransactions; transaction++) begin + ax = new(); + ax.ax_addr = BufferBaseAddr + axi_addr_t'(transaction * WriteBeats * + (TbAxiDataWidthFull / 8)); + ax.ax_id = TbAxiIdWidthFull'(transaction + 1); + ax.ax_len = WriteBeats - 1; + ax.ax_size = $clog2(TbAxiDataWidthFull / 8); + ax.ax_burst = axi_pkg::BURST_INCR; + axi_drv.send_aw(ax); + end + + for (int unsigned transaction = 0; transaction < NumTransactions; transaction++) begin + transaction_addr = BufferBaseAddr + axi_addr_t'(transaction * WriteBeats * + (TbAxiDataWidthFull / 8)); + for (int unsigned beat = 0; beat < WriteBeats; beat++) begin + w = new(); + w.w_data = slow_stress_data(transaction_addr, beat); + w.w_strb = '1; + w.w_last = beat == WriteBeats - 1; + axi_drv.send_w(w); + end + end + + for (int unsigned transaction = 0; transaction < NumTransactions; transaction++) begin + axi_drv.recv_b(b); + if ((b.b_resp != axi_pkg::RESP_OKAY) || + (b.b_id != TbAxiIdWidthFull'(transaction + 1))) begin + $error("[MIDEND-BUFFER] Write %0d returned id=%0d resp=%0d", + transaction, b.b_id, b.b_resp); + end + end + + // Four one-beat reads can occupy all read contexts and response entries. + for (int unsigned transaction = 0; transaction < NumTransactions; transaction++) begin + ax = new(); + ax.ax_addr = BufferBaseAddr + axi_addr_t'(transaction * WriteBeats * + (TbAxiDataWidthFull / 8)); + ax.ax_id = TbAxiIdWidthFull'(transaction + 1); + ax.ax_len = '0; + ax.ax_size = $clog2(TbAxiDataWidthFull / 8); + ax.ax_burst = axi_pkg::BURST_INCR; + axi_drv.send_ar(ax); + end + + for (int unsigned transaction = 0; transaction < NumTransactions; transaction++) begin + axi_drv.recv_r(r); + transaction_addr = BufferBaseAddr + axi_addr_t'(transaction * WriteBeats * + (TbAxiDataWidthFull / 8)); + if ((r.r_resp != axi_pkg::RESP_OKAY) || !r.r_last || + (r.r_id != TbAxiIdWidthFull'(transaction + 1)) || + (r.r_data != slow_stress_data(transaction_addr, 0))) begin + $error("[MIDEND-BUFFER] Read %0d returned id=%0d data=0x%016x last=%0b resp=%0d", + transaction, r.r_id, r.r_data, r.r_last, r.r_resp); + end + end + endtask + task automatic run_performance_smoke(input axi_ctrl_master_t axi_drv); localparam int unsigned NumCases = 5; localparam axi_addr_t PerfBaseAddr = axi_addr_t'(32'h8000_8000); @@ -852,7 +924,10 @@ module axi_hyper_tb if (reg_error != 1'b0) $error("unexpected error"); endtask - task automatic check_atomic_add(input axi_ctrl_master_t axi_drv); + task automatic check_atomic_add( + input axi_ctrl_master_t axi_drv, + input reg_bus_master_t reg_drv + ); localparam axi_addr_t AtomicAddr = axi_addr_t'(32'h8000_0200); localparam logic [31:0] InitialValue = 32'h1234_5678; localparam logic [31:0] Addend = 32'h0102_0304; @@ -860,6 +935,8 @@ module axi_hyper_tb axi_ctrl_master_t::w_beat_t w = new(); axi_ctrl_master_t::b_beat_t b; axi_ctrl_master_t::r_beat_t r; + logic [31:0] status; + logic reg_error; $display("==========================="); $display("= Atomic add ="); @@ -906,6 +983,12 @@ module axi_hyper_tb $error("[ATOMIC] Unsupported operation returned rresp=%0d bresp=%0d", r.r_resp, b.b_resp); end + reg_drv.send_read(32'h54, status, reg_error); + if ((reg_error != 1'b0) || !status[0]) begin + $error("[ATOMIC] Unsupported operation did not set sticky error status"); + end + reg_drv.send_write(32'h54, 32'h1, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); axi_check_subword(axi_drv, AtomicAddr, InitialValue + Addend, 2); // A malformed multi-beat atomic must drain all W beats before returning an error. @@ -928,6 +1011,12 @@ module axi_hyper_tb $error("[ATOMIC] Multi-beat operation returned rresp=%0d bresp=%0d", r.r_resp, b.b_resp); end + reg_drv.send_read(32'h54, status, reg_error); + if ((reg_error != 1'b0) || !status[0]) begin + $error("[ATOMIC] Malformed operation did not set sticky error status"); + end + reg_drv.send_write(32'h54, 32'h1, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); axi_write_subword(axi_drv, AtomicAddr, 32'h89ab_cdef, 2); axi_check_subword(axi_drv, AtomicAddr, 32'h89ab_cdef, 2); endtask @@ -1018,6 +1107,8 @@ module axi_hyper_tb automatic logic [63:0] divider_cycle_snapshot; automatic logic [63:0] div2_write_cycles; automatic logic [63:0] div4_write_cycles; + automatic logic [31:0] iso_saved_t_burst_max; + automatic int unsigned iso_test_dividers[4] = '{2, 4, 8, 16}; // Reset the AXI drivers and scoreboards end_of_sim = 1'b0; @@ -1086,6 +1177,7 @@ module axi_hyper_tb end end + check_midend_buffering(axi_ctrl_mst); run_performance_smoke(axi_ctrl_mst); run_slow_backpressure_test(axi_ctrl_mst, reg_master); check_config_barrier(axi_ctrl_mst, reg_master); @@ -1093,9 +1185,31 @@ module axi_hyper_tb check_cross_chip_burst(axi_ctrl_mst, reg_master); check_large_rule_distance(axi_ctrl_mst); check_range_edges(axi_ctrl_mst, reg_master); - check_atomic_add(axi_ctrl_mst); + check_atomic_add(axi_ctrl_mst, reg_master); check_atomic_range_errors(axi_ctrl_mst, reg_master); + if (TbDutVariant == 0) begin + $display("==========================="); + $display("= Isochronous backpressure ="); + $display("==========================="); + reg_master.send_read(32'h8, iso_saved_t_burst_max, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected t_burst_max read error"); + foreach (iso_test_dividers[i]) begin + // Keep each data phase near 2 us, leaving margin for CA and access latency. + reg_master.send_write(32'h8, 400 / iso_test_dividers[i], '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected t_burst_max update error"); + reg_master.send_write(32'h78, iso_test_dividers[i], '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected divider update error"); + axi_write_slow(axi_ctrl_mst, 32'h8001_0000 + i * 32'h100, 16, 0); + axi_read_slow_check(axi_ctrl_mst, 32'h8001_0000 + i * 32'h100, 16, 128); + end + + reg_master.send_write(32'h78, 8'd2, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected divider restore error"); + reg_master.send_write(32'h8, iso_saved_t_burst_max, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected t_burst_max restore error"); + end + if (NumPhys == 1) begin check_unaligned_word_access(axi_ctrl_mst); end diff --git a/test/hyperbus_atomic_handler_tb.sv b/test/hyperbus_atomic_handler_tb.sv new file mode 100644 index 0000000..b4f027d --- /dev/null +++ b/test/hyperbus_atomic_handler_tb.sv @@ -0,0 +1,361 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +`include "hyperbus/typedef.svh" + +module hyperbus_atomic_handler_tb; + + localparam int unsigned HostAddrWidth = 32; + localparam int unsigned HostDataWidth = 64; + + typedef logic [HostAddrWidth-1:0] host_addr_t; + typedef logic [HostDataWidth-1:0] host_data_t; + typedef logic [HostDataWidth/8-1:0] host_strb_t; + + `HYPERBUS_TYPEDEF_HOST_ALL_CT(tb_host, host_addr_t, host_data_t, host_strb_t) + + logic clk; + logic rst_n; + logic start; + logic request_valid; + tb_host_cmd_t request; + logic active; + logic completed; + tb_host_cmd_t command; + logic command_valid; + logic command_ready; + tb_host_w_t host_w; + logic host_w_valid; + logic host_w_ready; + tb_host_r_t host_r; + logic host_r_valid; + logic host_r_ready; + tb_host_wrsp_t host_wrsp; + logic host_wrsp_valid; + logic host_wrsp_ready; + tb_host_r_t read; + logic read_valid; + logic read_ready; + tb_host_w_t write; + logic write_valid; + logic write_ready; + logic write_rsp_error; + logic write_rsp_valid; + logic write_rsp_ready; + + hyperbus_atomic_handler #( + .HostAddrWidth ( HostAddrWidth ), + .HostDataWidth ( HostDataWidth ), + .host_cmd_t ( tb_host_cmd_t ), + .host_w_t ( tb_host_w_t ), + .host_r_t ( tb_host_r_t ), + .host_wrsp_t ( tb_host_wrsp_t ) + ) i_dut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .start_i ( start ), + .request_valid_i ( request_valid ), + .request_i ( request ), + .active_o ( active ), + .completed_o ( completed ), + .command_o ( command ), + .command_valid_o ( command_valid ), + .command_ready_i ( command_ready ), + .host_w_i ( host_w ), + .host_w_valid_i ( host_w_valid ), + .host_w_ready_o ( host_w_ready ), + .host_r_o ( host_r ), + .host_r_valid_o ( host_r_valid ), + .host_r_ready_i ( host_r_ready ), + .host_wrsp_o ( host_wrsp ), + .host_wrsp_valid_o ( host_wrsp_valid ), + .host_wrsp_ready_i ( host_wrsp_ready ), + .read_i ( read ), + .read_valid_i ( read_valid ), + .read_ready_o ( read_ready ), + .write_o ( write ), + .write_valid_o ( write_valid ), + .write_ready_i ( write_ready ), + .write_rsp_error_i ( write_rsp_error ), + .write_rsp_valid_i ( write_rsp_valid ), + .write_rsp_ready_o ( write_rsp_ready ) + ); + + initial begin + clk = 1'b0; + forever #5ns clk = ~clk; + end + + task automatic check_operation( + input hyperbus_pkg::hyper_atomic_op_e operation, + input logic [31:0] old_value, + input logic [31:0] operand, + input logic [31:0] expected, + input logic [31:0] swap_value = '0, + input logic compare = 1'b0, + input logic write_error = 1'b0 + ); + localparam logic [31:0] UpperWord = 32'ha5a5_5a5a; + host_data_t old_data; + host_data_t operand_data; + host_data_t expected_data; + + old_data = {UpperWord, old_value}; + operand_data = compare ? {swap_value, operand} : host_data_t'(operand); + expected_data = {UpperWord, expected}; + + @(negedge clk); + request = '0; + request.write = 1'b1; + request.addr = 32'h8000_0100; + request.beats = 1; + request.size = compare ? 3'd3 : 3'd2; + request.burst = hyperbus_pkg::HyperBurstIncr; + request.atomic_op = operation; + request.atomic_return = 1'b1; + request.ordered = 1'b1; + request_valid = 1'b1; + start = 1'b1; + + @(negedge clk); + request_valid = 1'b0; + start = 1'b0; + + wait (host_w_ready); + @(negedge clk); + host_w = '{data: operand_data, strb: '1, last: 1'b1}; + host_w_valid = 1'b1; + @(negedge clk); + host_w_valid = 1'b0; + + wait (command_valid); + #1ps; + if (command.write || (command.size != 2)) begin + $error("Atomic %s issued an invalid read command", operation.name()); + end + + wait (read_ready); + @(negedge clk); + read = '0; + read.data = old_data; + read.resp = hyperbus_pkg::HyperRespOkay; + read.last = 1'b1; + read_valid = 1'b1; + @(negedge clk); + read_valid = 1'b0; + + wait (command_valid); + #1ps; + if (!command.write || (command.size != 2)) begin + $error("Atomic %s issued an invalid write command", operation.name()); + end + + wait (write_valid); + #1ps; + if ((write.data != expected_data) || (write.strb != 8'h0f) || !write.last) begin + $error("Atomic %s produced data 0x%016h strb 0x%02h, expected 0x%016h/0x0f", + operation.name(), write.data, write.strb, expected_data); + end + + wait (write_rsp_ready); + @(negedge clk); + write_rsp_error = write_error; + write_rsp_valid = 1'b1; + @(negedge clk); + write_rsp_error = 1'b0; + write_rsp_valid = 1'b0; + + wait (host_r_valid && host_wrsp_valid); + #1ps; + if ((host_r.data != old_data) || !host_r.last || + (host_r.resp != (write_error ? hyperbus_pkg::HyperRespAccessError : + hyperbus_pkg::HyperRespOkay)) || + (host_wrsp.resp != (write_error ? hyperbus_pkg::HyperRespAccessError : + hyperbus_pkg::HyperRespOkay)) || + (host_r.atomic_ok == write_error) || (host_wrsp.atomic_ok == write_error)) begin + $error("Atomic %s returned an invalid host response", operation.name()); + end + + // Accept the read response first and ensure the write response remains stable. + @(negedge clk); + host_r_ready = 1'b1; + #1ps; + if (completed) begin + $error("Atomic %s completed before both host responses were accepted", + operation.name()); + end + @(negedge clk); + host_r_ready = 1'b0; + #1ps; + if (host_r_valid || !host_wrsp_valid || !active || completed) begin + $error("Atomic %s did not hold its pending write response", operation.name()); + end + + host_wrsp_ready = 1'b1; + #1ps; + if (!completed) begin + $error("Atomic %s did not complete with its final response", operation.name()); + end + @(negedge clk); + host_wrsp_ready = 1'b0; + wait (!active); + endtask + + task automatic check_read_error; + @(negedge clk); + request = '0; + request.write = 1'b1; + request.addr = 32'h8000_0200; + request.beats = 1; + request.size = 3'd2; + request.burst = hyperbus_pkg::HyperBurstIncr; + request.atomic_op = hyperbus_pkg::HyperAtomicAdd; + request.atomic_return = 1'b1; + request.ordered = 1'b1; + request_valid = 1'b1; + start = 1'b1; + + @(negedge clk); + request_valid = 1'b0; + start = 1'b0; + wait (host_w_ready); + @(negedge clk); + host_w = '{data: 64'h1, strb: '1, last: 1'b1}; + host_w_valid = 1'b1; + @(negedge clk); + host_w_valid = 1'b0; + + wait (read_ready); + @(negedge clk); + read = '0; + read.resp = hyperbus_pkg::HyperRespAccessError; + read.last = 1'b1; + read_valid = 1'b1; + @(negedge clk); + read_valid = 1'b0; + + wait (host_r_valid && host_wrsp_valid); + #1ps; + if (command_valid || write_valid || + (host_r.resp != hyperbus_pkg::HyperRespAccessError) || + (host_wrsp.resp != hyperbus_pkg::HyperRespAccessError) || + host_r.atomic_ok || host_wrsp.atomic_ok) begin + $error("Atomic backend read error was not returned without a write"); + end + + host_r_ready = 1'b1; + host_wrsp_ready = 1'b1; + @(negedge clk); + host_r_ready = 1'b0; + host_wrsp_ready = 1'b0; + wait (!active); + endtask + + task automatic check_invalid_operand_drain; + @(negedge clk); + request = '0; + request.write = 1'b1; + request.addr = 32'h8000_0300; + request.beats = 2; + request.size = 3'd2; + request.burst = hyperbus_pkg::HyperBurstIncr; + request.atomic_op = hyperbus_pkg::HyperAtomicAdd; + request.atomic_return = 1'b1; + request_valid = 1'b0; + start = 1'b1; + + @(negedge clk); + start = 1'b0; + wait (host_w_ready); + @(negedge clk); + host_w = '{data: 64'hdead_beef, strb: '1, last: 1'b0}; + host_w_valid = 1'b1; + @(negedge clk); + host_w_valid = 1'b0; + #1ps; + if (!active || command_valid || host_r_valid || host_wrsp_valid) begin + $error("Rejected atomic did not continue draining its operand stream"); + end + + @(negedge clk); + host_w = '{data: 64'hfeed_cafe, strb: '1, last: 1'b1}; + host_w_valid = 1'b1; + @(negedge clk); + host_w_valid = 1'b0; + wait (host_r_valid && host_wrsp_valid); + #1ps; + if (command_valid || write_valid || + (host_r.resp != hyperbus_pkg::HyperRespAtomicError) || + (host_wrsp.resp != hyperbus_pkg::HyperRespAtomicError)) begin + $error("Rejected atomic operand drain returned an invalid response"); + end + + host_r_ready = 1'b1; + host_wrsp_ready = 1'b1; + @(negedge clk); + host_r_ready = 1'b0; + host_wrsp_ready = 1'b0; + wait (!active); + endtask + + initial begin + rst_n = 1'b0; + start = 1'b0; + request_valid = 1'b0; + request = '0; + command_ready = 1'b1; + host_w = '0; + host_w_valid = 1'b0; + host_r_ready = 1'b0; + host_wrsp_ready = 1'b0; + read = '0; + read_valid = 1'b0; + write_ready = 1'b1; + write_rsp_error = 1'b0; + write_rsp_valid = 1'b0; + + repeat (4) @(posedge clk); + rst_n = 1'b1; + repeat (2) @(posedge clk); + + check_operation(hyperbus_pkg::HyperAtomicSwap, + 32'h1234_5678, 32'h89ab_cdef, 32'h89ab_cdef); + check_operation(hyperbus_pkg::HyperAtomicCompare, + 32'h1234_5678, 32'h1234_5678, 32'hfeed_cafe, 32'hfeed_cafe, 1'b1); + check_operation(hyperbus_pkg::HyperAtomicCompare, + 32'h1234_5678, 32'h8765_4321, 32'h1234_5678, 32'hfeed_cafe, 1'b1); + check_operation(hyperbus_pkg::HyperAtomicAdd, + 32'h1234_5678, 32'h0102_0304, 32'h1336_597c); + check_operation(hyperbus_pkg::HyperAtomicAnd, + 32'hf0f0_55aa, 32'h0ff0_f00f, 32'h00f0_500a); + check_operation(hyperbus_pkg::HyperAtomicClear, + 32'hffff_55aa, 32'h0ff0_f00f, 32'hf00f_05a0); + check_operation(hyperbus_pkg::HyperAtomicXor, + 32'hf0f0_55aa, 32'h0ff0_f00f, 32'hff00_a5a5); + check_operation(hyperbus_pkg::HyperAtomicSet, + 32'hf0f0_55aa, 32'h0ff0_f00f, 32'hfff0_f5af); + check_operation(hyperbus_pkg::HyperAtomicSignedMax, + 32'hffff_fffb, 32'h0000_0003, 32'h0000_0003); + check_operation(hyperbus_pkg::HyperAtomicSignedMin, + 32'h0000_0003, 32'hffff_fffb, 32'hffff_fffb); + check_operation(hyperbus_pkg::HyperAtomicUnsignedMax, + 32'h0000_0002, 32'hffff_fff0, 32'hffff_fff0); + check_operation(hyperbus_pkg::HyperAtomicUnsignedMin, + 32'h0000_0002, 32'hffff_fff0, 32'h0000_0002); + check_operation(hyperbus_pkg::HyperAtomicAdd, + 32'h1234_5678, 32'h0102_0304, 32'h1336_597c, + '0, 1'b0, 1'b1); + check_read_error(); + check_invalid_operand_drain(); + + $display("Atomic handler operation and protocol tests passed"); + $finish; + end + + initial begin + #100us; + $fatal(1, "Atomic handler test timed out"); + end + +endmodule : hyperbus_atomic_handler_tb