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 de23acc..54a50e2 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 } @@ -39,28 +42,41 @@ 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_pkg.sv - - src/hyperbus_clk_gen.sv - - src/hyperbus_clock_diff_out.sv - - src/hyperbus_w2phy.sv - - src/hyperbus_phy2r.sv - - src/hyperbus_ddr_out.sv - - src/hyperbus_trx.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/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/hyperbus_phy_if.sv - - src/hyperbus_axi.sv + - src/backend/hyperbus_phy.sv + - src/hyperbus_axi_frontend.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/backend/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 - - src/hyperbus.sv + - test/hyperbus_atomic_handler_tb.sv + - test/hyperbus_cfg_regs_tb.sv + - test/axi_pre_aw_drain_tb.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..9458acf --- /dev/null +++ b/docs/regs/hyperbus_cfg_regs.md @@ -0,0 +1,1135 @@ + + +## hyperbus_cfg_regs address map + +- Absolute Address: 0x0 +- Base Offset: 0x0 +- Size: 0x1000 + +

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

+ +|Offset| Identifier |Name| +|------|------------|----| +| 0x000| global_cfg | — | +| 0x100| frontend | — | +| 0x200| backend | — | +| 0x300| phy_0 | — | +| 0x340| phy_1 | — | +| 0x400| chip_0 | — | +| 0x440| chip_1 | — | +| 0x480| chip_2 | — | +| 0x4C0| chip_3 | — | +| 0x500| chip_4 | — | +| 0x540| chip_5 | — | +| 0x580| chip_6 | — | +| 0x5C0| chip_7 | — | +| 0xFFC|reserved_top| — | + +## global_cfg register file + +- Absolute Address: 0x0 +- Base Offset: 0x0 +- Size: 0x14 + +|Offset| Identifier |Name| +|------|--------------|----| +| 0x00 | ip_version | — | +| 0x04 |reg_if_version| — | +| 0x08 | capability | — | +| 0x0C | command | — | +| 0x10 | status | — | + +### ip_version register + +- Absolute Address: 0x0 +- Base Offset: 0x0 +- Size: 0x4 + +

IP version (major.minor.patch.revision).

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +| 7:0 | revision | r | 0x0 | — | +| 15:8| patch | r | 0x9 | — | +|23:16| minor | r | 0x0 | — | +|31:24| major | r | 0x0 | — | + +### reg_if_version register + +- Absolute Address: 0x4 +- Base Offset: 0x4 +- Size: 0x4 + +

Register interface version.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +| 15:0| minor | r | 0x0 | — | +|31:16| major | r | 0x1 | — | + +### capability register + +- Absolute Address: 0x8 +- Base Offset: 0x8 +- Size: 0x4 + +

Hardware-provided implementation capabilities.

+ +|Bits| Identifier |Access|Reset|Name| +|----|------------------|------|-----|----| +| 7:0| num_chips | r | 0x0 | — | +|15:8| num_phys | r | 0x0 | — | +| 16 | per_chip_cfg | r | 0x0 | — | +| 17 | per_phy_cfg | r | 0x0 | — | +| 18 | chip_enable | r | 0x0 | — | +| 19 | clock_divider | r | 0x0 | — | +| 20 | staged_apply | r | 0x0 | — | +| 21 | error_status | r | 0x0 | — | +| 22 |rwds_sample_timing| r | 0x0 | — | +| 23 | rwds_oe_timing | r | 0x0 | — | + +#### num_chips field + +

Number of implemented chip selects.

+ +#### num_phys field + +

Number of implemented PHY lanes.

+ +#### per_chip_cfg field + +

Each chip record controls its corresponding chip rather than chip 0 being shared.

+ +#### per_phy_cfg field + +

Each PHY record controls its corresponding lane rather than PHY 0 being shared.

+ +#### chip_enable field + +

Per-chip enable bits participate in address decoding.

+ +#### clock_divider field + +

CLOCK_CFG controls an implemented backend clock divider.

+ +#### staged_apply field + +

FLUSH and APPLY commands implement a staged configuration barrier.

+ +#### error_status field + +

STATUS reports sticky controller errors.

+ +#### rwds_sample_timing field + +

Per-chip RWDS sample timing is implemented.

+ +#### rwds_oe_timing field + +

Per-PHY RWDS output-enable timing is implemented.

+ +### command register + +- Absolute Address: 0xC +- Base Offset: 0xC +- Size: 0x4 + +

Single-cycle write-one command strobes, active when capability.staged_apply is set; otherwise writes are retained only as inert pulses.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 0 | flush | rw | 0x0 | — | +| 1 | apply | rw | 0x0 | — | + +### status register + +- Absolute Address: 0x10 +- Base Offset: 0x10 +- Size: 0x4 + +

Controller status.

+ +|Bits| Identifier | Access |Reset|Name| +|----|------------|---------|-----|----| +| 0 |decode_error|rw, woclr| 0x0 | — | +| 1 | busy | r | 0x0 | — | +| 2 | dirty | r | 0x0 | — | + +## frontend register file + +- Absolute Address: 0x100 +- Base Offset: 0x100 +- Size: 0x4 + +|Offset| Identifier |Name| +|------|------------|----| +| 0x0 |frontend_cfg| — | + +### frontend_cfg register + +- Absolute Address: 0x100 +- Base Offset: 0x0 +- Size: 0x4 + +

Frontend configuration. dual_phy must be 0 or 1.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 0 | dual_phy | rw | 0x1 | — | + +## backend register file + +- Absolute Address: 0x200 +- Base Offset: 0x200 +- Size: 0x4 + +|Offset|Identifier|Name| +|------|----------|----| +| 0x0 | clock_cfg| — | + +### clock_cfg register + +- Absolute Address: 0x200 +- Base Offset: 0x0 +- Size: 0x4 + +

Backend clock-divider configuration, active when capability.clock_divider is set; divider values must be at least 2.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| divider | rw | 0x8 | — | + +## phy_0 register file + +- Absolute Address: 0x300 +- Base Offset: 0x300 +- Size: 0x8 + +|Offset| Identifier|Name| +|------|-----------|----| +| 0x0 | tx_delay | — | +| 0x4 |rwds_timing| — | + +### tx_delay register + +- Absolute Address: 0x300 +- Base Offset: 0x0 +- Size: 0x4 + +

PHY transmit delay. PHY 0 is shared when capability.per_phy_cfg is clear.

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

PHY RWDS output-enable timing. rwds_oe_setup_cycles must be at most 15 and is active when capability.rwds_oe_timing is set.

+ +|Bits| Identifier |Access|Reset|Name| +|----|--------------------|------|-----|----| +| 7:0|rwds_oe_setup_cycles| rw | 0x1 | — | + +## phy_1 register file + +- Absolute Address: 0x340 +- Base Offset: 0x340 +- Size: 0x8 + +|Offset| Identifier|Name| +|------|-----------|----| +| 0x0 | tx_delay | — | +| 0x4 |rwds_timing| — | + +### tx_delay register + +- Absolute Address: 0x340 +- Base Offset: 0x0 +- Size: 0x4 + +

PHY transmit delay. PHY 0 is shared when capability.per_phy_cfg is clear.

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

PHY RWDS output-enable timing. rwds_oe_setup_cycles must be at most 15 and is active when capability.rwds_oe_timing is set.

+ +|Bits| Identifier |Access|Reset|Name| +|----|--------------------|------|-----|----| +| 7:0|rwds_oe_setup_cycles| rw | 0x1 | — | + +## chip_0 register file + +- Absolute Address: 0x400 +- Base Offset: 0x400 +- Size: 0x1C + +|Offset| Identifier|Name| +|------|-----------|----| +| 0x00 | range_base| — | +| 0x04 |range_bound| — | +| 0x08 |address_cfg| — | +| 0x0C |latency_cfg| — | +| 0x10 | burst_cfg | — | +| 0x14 |chip_timing| — | +| 0x18 | rx_delay | — | + +### range_base register + +- Absolute Address: 0x400 +- Base Offset: 0x0 +- Size: 0x4 + +

Inclusive 4 MiB-aligned chip address range base.

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

Exclusive 4 MiB-aligned chip address range bound.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x1 | — | + +### address_cfg register + +- Absolute Address: 0x408 +- Base Offset: 0x8 +- Size: 0x4 + +

Chip address-space interpretation and enable. address_space and enable must be 0 or 1, and address_mask_msb must be at most 31. Chip 0 is shared when capability.per_chip_cfg is clear; enable is active when capability.chip_enable is set.

+ +|Bits| Identifier |Access|Reset|Name| +|----|----------------|------|-----|----| +| 7:0| address_space | rw | 0x0 | — | +|15:8|address_mask_msb| rw | 0x19| — | +| 16 | enable | rw | 0x1 | — | + +### latency_cfg register + +- Absolute Address: 0x40C +- Base Offset: 0xC +- Size: 0x4 + +

Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier |Access|Reset|Name| +|----|---------------------|------|-----|----| +| 7:0| t_latency_access | rw | 0x6 | — | +|15:8| rwds_sample_delay | rw | 0x0 | — | +| 16 |en_latency_additional| rw | 0x0 | — | + +### burst_cfg register + +- Absolute Address: 0x410 +- Base Offset: 0x10 +- Size: 0x4 + +

Chip burst configuration. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier|Access|Reset|Name| +|----|-----------|------|-----|----| +|15:0|t_burst_max| rw |0x15E| — | + +### chip_timing register + +- Absolute Address: 0x414 +- Base Offset: 0x14 +- Size: 0x4 + +

Chip-select and read/write timing; byte-backed timing values must be at most 15. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +| Bits| Identifier |Access|Reset|Name| +|-----|---------------------|------|-----|----| +| 7:0 |t_read_write_recovery| rw | 0x6 | — | +| 15:8| t_csh_cycles | rw | 0x1 | — | +|23:16| csn_to_ck_cycles | rw | 0x0 | — | + +### rx_delay register + +- Absolute Address: 0x418 +- Base Offset: 0x18 +- Size: 0x4 + +

Chip receive delay. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| value | rw | 0x10| — | + +## chip_1 register file + +- Absolute Address: 0x440 +- Base Offset: 0x440 +- Size: 0x1C + +|Offset| Identifier|Name| +|------|-----------|----| +| 0x00 | range_base| — | +| 0x04 |range_bound| — | +| 0x08 |address_cfg| — | +| 0x0C |latency_cfg| — | +| 0x10 | burst_cfg | — | +| 0x14 |chip_timing| — | +| 0x18 | rx_delay | — | + +### range_base register + +- Absolute Address: 0x440 +- Base Offset: 0x0 +- Size: 0x4 + +

Inclusive 4 MiB-aligned chip address range base.

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

Exclusive 4 MiB-aligned chip address range bound.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x2 | — | + +### address_cfg register + +- Absolute Address: 0x448 +- Base Offset: 0x8 +- Size: 0x4 + +

Chip address-space interpretation and enable. address_space and enable must be 0 or 1, and address_mask_msb must be at most 31. Chip 0 is shared when capability.per_chip_cfg is clear; enable is active when capability.chip_enable is set.

+ +|Bits| Identifier |Access|Reset|Name| +|----|----------------|------|-----|----| +| 7:0| address_space | rw | 0x0 | — | +|15:8|address_mask_msb| rw | 0x19| — | +| 16 | enable | rw | 0x1 | — | + +### latency_cfg register + +- Absolute Address: 0x44C +- Base Offset: 0xC +- Size: 0x4 + +

Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier |Access|Reset|Name| +|----|---------------------|------|-----|----| +| 7:0| t_latency_access | rw | 0x6 | — | +|15:8| rwds_sample_delay | rw | 0x0 | — | +| 16 |en_latency_additional| rw | 0x0 | — | + +### burst_cfg register + +- Absolute Address: 0x450 +- Base Offset: 0x10 +- Size: 0x4 + +

Chip burst configuration. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier|Access|Reset|Name| +|----|-----------|------|-----|----| +|15:0|t_burst_max| rw |0x15E| — | + +### chip_timing register + +- Absolute Address: 0x454 +- Base Offset: 0x14 +- Size: 0x4 + +

Chip-select and read/write timing; byte-backed timing values must be at most 15. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +| Bits| Identifier |Access|Reset|Name| +|-----|---------------------|------|-----|----| +| 7:0 |t_read_write_recovery| rw | 0x6 | — | +| 15:8| t_csh_cycles | rw | 0x1 | — | +|23:16| csn_to_ck_cycles | rw | 0x0 | — | + +### rx_delay register + +- Absolute Address: 0x458 +- Base Offset: 0x18 +- Size: 0x4 + +

Chip receive delay. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| value | rw | 0x10| — | + +## chip_2 register file + +- Absolute Address: 0x480 +- Base Offset: 0x480 +- Size: 0x1C + +|Offset| Identifier|Name| +|------|-----------|----| +| 0x00 | range_base| — | +| 0x04 |range_bound| — | +| 0x08 |address_cfg| — | +| 0x0C |latency_cfg| — | +| 0x10 | burst_cfg | — | +| 0x14 |chip_timing| — | +| 0x18 | rx_delay | — | + +### range_base register + +- Absolute Address: 0x480 +- Base Offset: 0x0 +- Size: 0x4 + +

Inclusive 4 MiB-aligned chip address range base.

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

Exclusive 4 MiB-aligned chip address range bound.

+ +| Bits|Identifier|Access|Reset|Name| +|-----|----------|------|-----|----| +|31:22| value | rw | 0x3 | — | + +### address_cfg register + +- Absolute Address: 0x488 +- Base Offset: 0x8 +- Size: 0x4 + +

Chip address-space interpretation and enable. address_space and enable must be 0 or 1, and address_mask_msb must be at most 31. Chip 0 is shared when capability.per_chip_cfg is clear; enable is active when capability.chip_enable is set.

+ +|Bits| Identifier |Access|Reset|Name| +|----|----------------|------|-----|----| +| 7:0| address_space | rw | 0x0 | — | +|15:8|address_mask_msb| rw | 0x19| — | +| 16 | enable | rw | 0x1 | — | + +### latency_cfg register + +- Absolute Address: 0x48C +- Base Offset: 0xC +- Size: 0x4 + +

Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier |Access|Reset|Name| +|----|---------------------|------|-----|----| +| 7:0| t_latency_access | rw | 0x6 | — | +|15:8| rwds_sample_delay | rw | 0x0 | — | +| 16 |en_latency_additional| rw | 0x0 | — | + +### burst_cfg register + +- Absolute Address: 0x490 +- Base Offset: 0x10 +- Size: 0x4 + +

Chip burst configuration. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier|Access|Reset|Name| +|----|-----------|------|-----|----| +|15:0|t_burst_max| rw |0x15E| — | + +### chip_timing register + +- Absolute Address: 0x494 +- Base Offset: 0x14 +- Size: 0x4 + +

Chip-select and read/write timing; byte-backed timing values must be at most 15. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +| Bits| Identifier |Access|Reset|Name| +|-----|---------------------|------|-----|----| +| 7:0 |t_read_write_recovery| rw | 0x6 | — | +| 15:8| t_csh_cycles | rw | 0x1 | — | +|23:16| csn_to_ck_cycles | rw | 0x0 | — | + +### rx_delay register + +- Absolute Address: 0x498 +- Base Offset: 0x18 +- Size: 0x4 + +

Chip receive delay. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| value | rw | 0x10| — | + +## chip_3 register file + +- Absolute Address: 0x4C0 +- Base Offset: 0x4C0 +- Size: 0x1C + +|Offset| Identifier|Name| +|------|-----------|----| +| 0x00 | range_base| — | +| 0x04 |range_bound| — | +| 0x08 |address_cfg| — | +| 0x0C |latency_cfg| — | +| 0x10 | burst_cfg | — | +| 0x14 |chip_timing| — | +| 0x18 | rx_delay | — | + +### range_base register + +- Absolute Address: 0x4C0 +- Base Offset: 0x0 +- Size: 0x4 + +

Inclusive 4 MiB-aligned chip address range base.

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

Exclusive 4 MiB-aligned chip address range bound.

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

Chip address-space interpretation and enable. address_space and enable must be 0 or 1, and address_mask_msb must be at most 31. Chip 0 is shared when capability.per_chip_cfg is clear; enable is active when capability.chip_enable is set.

+ +|Bits| Identifier |Access|Reset|Name| +|----|----------------|------|-----|----| +| 7:0| address_space | rw | 0x0 | — | +|15:8|address_mask_msb| rw | 0x19| — | +| 16 | enable | rw | 0x1 | — | + +### latency_cfg register + +- Absolute Address: 0x4CC +- Base Offset: 0xC +- Size: 0x4 + +

Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier |Access|Reset|Name| +|----|---------------------|------|-----|----| +| 7:0| t_latency_access | rw | 0x6 | — | +|15:8| rwds_sample_delay | rw | 0x0 | — | +| 16 |en_latency_additional| rw | 0x0 | — | + +### burst_cfg register + +- Absolute Address: 0x4D0 +- Base Offset: 0x10 +- Size: 0x4 + +

Chip burst configuration. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier|Access|Reset|Name| +|----|-----------|------|-----|----| +|15:0|t_burst_max| rw |0x15E| — | + +### chip_timing register + +- Absolute Address: 0x4D4 +- Base Offset: 0x14 +- Size: 0x4 + +

Chip-select and read/write timing; byte-backed timing values must be at most 15. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +| Bits| Identifier |Access|Reset|Name| +|-----|---------------------|------|-----|----| +| 7:0 |t_read_write_recovery| rw | 0x6 | — | +| 15:8| t_csh_cycles | rw | 0x1 | — | +|23:16| csn_to_ck_cycles | rw | 0x0 | — | + +### rx_delay register + +- Absolute Address: 0x4D8 +- Base Offset: 0x18 +- Size: 0x4 + +

Chip receive delay. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| value | rw | 0x10| — | + +## chip_4 register file + +- Absolute Address: 0x500 +- Base Offset: 0x500 +- Size: 0x1C + +|Offset| Identifier|Name| +|------|-----------|----| +| 0x00 | range_base| — | +| 0x04 |range_bound| — | +| 0x08 |address_cfg| — | +| 0x0C |latency_cfg| — | +| 0x10 | burst_cfg | — | +| 0x14 |chip_timing| — | +| 0x18 | rx_delay | — | + +### range_base register + +- Absolute Address: 0x500 +- Base Offset: 0x0 +- Size: 0x4 + +

Inclusive 4 MiB-aligned chip address range base.

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

Exclusive 4 MiB-aligned chip address range bound.

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

Chip address-space interpretation and enable. address_space and enable must be 0 or 1, and address_mask_msb must be at most 31. Chip 0 is shared when capability.per_chip_cfg is clear; enable is active when capability.chip_enable is set.

+ +|Bits| Identifier |Access|Reset|Name| +|----|----------------|------|-----|----| +| 7:0| address_space | rw | 0x0 | — | +|15:8|address_mask_msb| rw | 0x19| — | +| 16 | enable | rw | 0x1 | — | + +### latency_cfg register + +- Absolute Address: 0x50C +- Base Offset: 0xC +- Size: 0x4 + +

Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier |Access|Reset|Name| +|----|---------------------|------|-----|----| +| 7:0| t_latency_access | rw | 0x6 | — | +|15:8| rwds_sample_delay | rw | 0x0 | — | +| 16 |en_latency_additional| rw | 0x0 | — | + +### burst_cfg register + +- Absolute Address: 0x510 +- Base Offset: 0x10 +- Size: 0x4 + +

Chip burst configuration. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier|Access|Reset|Name| +|----|-----------|------|-----|----| +|15:0|t_burst_max| rw |0x15E| — | + +### chip_timing register + +- Absolute Address: 0x514 +- Base Offset: 0x14 +- Size: 0x4 + +

Chip-select and read/write timing; byte-backed timing values must be at most 15. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +| Bits| Identifier |Access|Reset|Name| +|-----|---------------------|------|-----|----| +| 7:0 |t_read_write_recovery| rw | 0x6 | — | +| 15:8| t_csh_cycles | rw | 0x1 | — | +|23:16| csn_to_ck_cycles | rw | 0x0 | — | + +### rx_delay register + +- Absolute Address: 0x518 +- Base Offset: 0x18 +- Size: 0x4 + +

Chip receive delay. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| value | rw | 0x10| — | + +## chip_5 register file + +- Absolute Address: 0x540 +- Base Offset: 0x540 +- Size: 0x1C + +|Offset| Identifier|Name| +|------|-----------|----| +| 0x00 | range_base| — | +| 0x04 |range_bound| — | +| 0x08 |address_cfg| — | +| 0x0C |latency_cfg| — | +| 0x10 | burst_cfg | — | +| 0x14 |chip_timing| — | +| 0x18 | rx_delay | — | + +### range_base register + +- Absolute Address: 0x540 +- Base Offset: 0x0 +- Size: 0x4 + +

Inclusive 4 MiB-aligned chip address range base.

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

Exclusive 4 MiB-aligned chip address range bound.

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

Chip address-space interpretation and enable. address_space and enable must be 0 or 1, and address_mask_msb must be at most 31. Chip 0 is shared when capability.per_chip_cfg is clear; enable is active when capability.chip_enable is set.

+ +|Bits| Identifier |Access|Reset|Name| +|----|----------------|------|-----|----| +| 7:0| address_space | rw | 0x0 | — | +|15:8|address_mask_msb| rw | 0x19| — | +| 16 | enable | rw | 0x1 | — | + +### latency_cfg register + +- Absolute Address: 0x54C +- Base Offset: 0xC +- Size: 0x4 + +

Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier |Access|Reset|Name| +|----|---------------------|------|-----|----| +| 7:0| t_latency_access | rw | 0x6 | — | +|15:8| rwds_sample_delay | rw | 0x0 | — | +| 16 |en_latency_additional| rw | 0x0 | — | + +### burst_cfg register + +- Absolute Address: 0x550 +- Base Offset: 0x10 +- Size: 0x4 + +

Chip burst configuration. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier|Access|Reset|Name| +|----|-----------|------|-----|----| +|15:0|t_burst_max| rw |0x15E| — | + +### chip_timing register + +- Absolute Address: 0x554 +- Base Offset: 0x14 +- Size: 0x4 + +

Chip-select and read/write timing; byte-backed timing values must be at most 15. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +| Bits| Identifier |Access|Reset|Name| +|-----|---------------------|------|-----|----| +| 7:0 |t_read_write_recovery| rw | 0x6 | — | +| 15:8| t_csh_cycles | rw | 0x1 | — | +|23:16| csn_to_ck_cycles | rw | 0x0 | — | + +### rx_delay register + +- Absolute Address: 0x558 +- Base Offset: 0x18 +- Size: 0x4 + +

Chip receive delay. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| value | rw | 0x10| — | + +## chip_6 register file + +- Absolute Address: 0x580 +- Base Offset: 0x580 +- Size: 0x1C + +|Offset| Identifier|Name| +|------|-----------|----| +| 0x00 | range_base| — | +| 0x04 |range_bound| — | +| 0x08 |address_cfg| — | +| 0x0C |latency_cfg| — | +| 0x10 | burst_cfg | — | +| 0x14 |chip_timing| — | +| 0x18 | rx_delay | — | + +### range_base register + +- Absolute Address: 0x580 +- Base Offset: 0x0 +- Size: 0x4 + +

Inclusive 4 MiB-aligned chip address range base.

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

Exclusive 4 MiB-aligned chip address range bound.

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

Chip address-space interpretation and enable. address_space and enable must be 0 or 1, and address_mask_msb must be at most 31. Chip 0 is shared when capability.per_chip_cfg is clear; enable is active when capability.chip_enable is set.

+ +|Bits| Identifier |Access|Reset|Name| +|----|----------------|------|-----|----| +| 7:0| address_space | rw | 0x0 | — | +|15:8|address_mask_msb| rw | 0x19| — | +| 16 | enable | rw | 0x1 | — | + +### latency_cfg register + +- Absolute Address: 0x58C +- Base Offset: 0xC +- Size: 0x4 + +

Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier |Access|Reset|Name| +|----|---------------------|------|-----|----| +| 7:0| t_latency_access | rw | 0x6 | — | +|15:8| rwds_sample_delay | rw | 0x0 | — | +| 16 |en_latency_additional| rw | 0x0 | — | + +### burst_cfg register + +- Absolute Address: 0x590 +- Base Offset: 0x10 +- Size: 0x4 + +

Chip burst configuration. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier|Access|Reset|Name| +|----|-----------|------|-----|----| +|15:0|t_burst_max| rw |0x15E| — | + +### chip_timing register + +- Absolute Address: 0x594 +- Base Offset: 0x14 +- Size: 0x4 + +

Chip-select and read/write timing; byte-backed timing values must be at most 15. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +| Bits| Identifier |Access|Reset|Name| +|-----|---------------------|------|-----|----| +| 7:0 |t_read_write_recovery| rw | 0x6 | — | +| 15:8| t_csh_cycles | rw | 0x1 | — | +|23:16| csn_to_ck_cycles | rw | 0x0 | — | + +### rx_delay register + +- Absolute Address: 0x598 +- Base Offset: 0x18 +- Size: 0x4 + +

Chip receive delay. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +| 7:0| value | rw | 0x10| — | + +## chip_7 register file + +- Absolute Address: 0x5C0 +- Base Offset: 0x5C0 +- Size: 0x1C + +|Offset| Identifier|Name| +|------|-----------|----| +| 0x00 | range_base| — | +| 0x04 |range_bound| — | +| 0x08 |address_cfg| — | +| 0x0C |latency_cfg| — | +| 0x10 | burst_cfg | — | +| 0x14 |chip_timing| — | +| 0x18 | rx_delay | — | + +### range_base register + +- Absolute Address: 0x5C0 +- Base Offset: 0x0 +- Size: 0x4 + +

Inclusive 4 MiB-aligned chip address range base.

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

Exclusive 4 MiB-aligned chip address range bound.

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

Chip address-space interpretation and enable. address_space and enable must be 0 or 1, and address_mask_msb must be at most 31. Chip 0 is shared when capability.per_chip_cfg is clear; enable is active when capability.chip_enable is set.

+ +|Bits| Identifier |Access|Reset|Name| +|----|----------------|------|-----|----| +| 7:0| address_space | rw | 0x0 | — | +|15:8|address_mask_msb| rw | 0x19| — | +| 16 | enable | rw | 0x1 | — | + +### latency_cfg register + +- Absolute Address: 0x5CC +- Base Offset: 0xC +- Size: 0x4 + +

Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier |Access|Reset|Name| +|----|---------------------|------|-----|----| +| 7:0| t_latency_access | rw | 0x6 | — | +|15:8| rwds_sample_delay | rw | 0x0 | — | +| 16 |en_latency_additional| rw | 0x0 | — | + +### burst_cfg register + +- Absolute Address: 0x5D0 +- Base Offset: 0x10 +- Size: 0x4 + +

Chip burst configuration. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +|Bits| Identifier|Access|Reset|Name| +|----|-----------|------|-----|----| +|15:0|t_burst_max| rw |0x15E| — | + +### chip_timing register + +- Absolute Address: 0x5D4 +- Base Offset: 0x14 +- Size: 0x4 + +

Chip-select and read/write timing; byte-backed timing values must be at most 15. Chip 0 is shared when capability.per_chip_cfg is clear.

+ +| Bits| Identifier |Access|Reset|Name| +|-----|---------------------|------|-----|----| +| 7:0 |t_read_write_recovery| rw | 0x6 | — | +| 15:8| t_csh_cycles | rw | 0x1 | — | +|23:16| csn_to_ck_cycles | rw | 0x0 | — | + +### rx_delay register + +- Absolute Address: 0x5D8 +- Base Offset: 0x18 +- Size: 0x4 + +

Chip receive delay. Chip 0 is shared when capability.per_chip_cfg is clear.

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

Reserved endpoint that fixes the register window size to 4 KiB.

+ +|Bits|Identifier|Access|Reset|Name| +|----|----------|------|-----|----| +|31:0| value | r | 0x0 | — | diff --git a/include/hyperbus/typedef.svh b/include/hyperbus/typedef.svh new file mode 100644 index 0000000..cd20bbb --- /dev/null +++ b/include/hyperbus/typedef.svh @@ -0,0 +1,114 @@ +// 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_ + +//////////////////////////////// +// Protocol-neutral host link // +//////////////////////////////// + +`define HYPERBUS_TYPEDEF_HOST_CMD_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_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_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_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) + +//////////////////////////// +// 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; \ + 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 [hyperbus_pkg::HyperNumChips-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/include/hyperbus_cfg_regs.h b/include/hyperbus_cfg_regs.h new file mode 100644 index 0000000..25b7b58 --- /dev/null +++ b/include/hyperbus_cfg_regs.h @@ -0,0 +1,438 @@ +// 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::ip_version +#define HYPERBUS_CFG_REGS__IP_VERSION__REVISION_bm 0xff +#define HYPERBUS_CFG_REGS__IP_VERSION__REVISION_bp 0 +#define HYPERBUS_CFG_REGS__IP_VERSION__REVISION_bw 8 +#define HYPERBUS_CFG_REGS__IP_VERSION__REVISION_reset 0x0 +#define HYPERBUS_CFG_REGS__IP_VERSION__PATCH_bm 0xff00 +#define HYPERBUS_CFG_REGS__IP_VERSION__PATCH_bp 8 +#define HYPERBUS_CFG_REGS__IP_VERSION__PATCH_bw 8 +#define HYPERBUS_CFG_REGS__IP_VERSION__PATCH_reset 0x9 +#define HYPERBUS_CFG_REGS__IP_VERSION__MINOR_bm 0xff0000 +#define HYPERBUS_CFG_REGS__IP_VERSION__MINOR_bp 16 +#define HYPERBUS_CFG_REGS__IP_VERSION__MINOR_bw 8 +#define HYPERBUS_CFG_REGS__IP_VERSION__MINOR_reset 0x0 +#define HYPERBUS_CFG_REGS__IP_VERSION__MAJOR_bm 0xff000000 +#define HYPERBUS_CFG_REGS__IP_VERSION__MAJOR_bp 24 +#define HYPERBUS_CFG_REGS__IP_VERSION__MAJOR_bw 8 +#define HYPERBUS_CFG_REGS__IP_VERSION__MAJOR_reset 0x0 + +// reg - hyperbus_cfg_regs::reg_if_version +#define HYPERBUS_CFG_REGS__REG_IF_VERSION__MINOR_bm 0xffff +#define HYPERBUS_CFG_REGS__REG_IF_VERSION__MINOR_bp 0 +#define HYPERBUS_CFG_REGS__REG_IF_VERSION__MINOR_bw 16 +#define HYPERBUS_CFG_REGS__REG_IF_VERSION__MINOR_reset 0x0 +#define HYPERBUS_CFG_REGS__REG_IF_VERSION__MAJOR_bm 0xffff0000 +#define HYPERBUS_CFG_REGS__REG_IF_VERSION__MAJOR_bp 16 +#define HYPERBUS_CFG_REGS__REG_IF_VERSION__MAJOR_bw 16 +#define HYPERBUS_CFG_REGS__REG_IF_VERSION__MAJOR_reset 0x1 + +// reg - hyperbus_cfg_regs::capability +#define HYPERBUS_CFG_REGS__CAPABILITY__NUM_CHIPS_bm 0xff +#define HYPERBUS_CFG_REGS__CAPABILITY__NUM_CHIPS_bp 0 +#define HYPERBUS_CFG_REGS__CAPABILITY__NUM_CHIPS_bw 8 +#define HYPERBUS_CFG_REGS__CAPABILITY__NUM_CHIPS_reset 0x0 +#define HYPERBUS_CFG_REGS__CAPABILITY__NUM_PHYS_bm 0xff00 +#define HYPERBUS_CFG_REGS__CAPABILITY__NUM_PHYS_bp 8 +#define HYPERBUS_CFG_REGS__CAPABILITY__NUM_PHYS_bw 8 +#define HYPERBUS_CFG_REGS__CAPABILITY__NUM_PHYS_reset 0x0 +#define HYPERBUS_CFG_REGS__CAPABILITY__PER_CHIP_CFG_bm 0x10000 +#define HYPERBUS_CFG_REGS__CAPABILITY__PER_CHIP_CFG_bp 16 +#define HYPERBUS_CFG_REGS__CAPABILITY__PER_CHIP_CFG_bw 1 +#define HYPERBUS_CFG_REGS__CAPABILITY__PER_CHIP_CFG_reset 0x0 +#define HYPERBUS_CFG_REGS__CAPABILITY__PER_PHY_CFG_bm 0x20000 +#define HYPERBUS_CFG_REGS__CAPABILITY__PER_PHY_CFG_bp 17 +#define HYPERBUS_CFG_REGS__CAPABILITY__PER_PHY_CFG_bw 1 +#define HYPERBUS_CFG_REGS__CAPABILITY__PER_PHY_CFG_reset 0x0 +#define HYPERBUS_CFG_REGS__CAPABILITY__CHIP_ENABLE_bm 0x40000 +#define HYPERBUS_CFG_REGS__CAPABILITY__CHIP_ENABLE_bp 18 +#define HYPERBUS_CFG_REGS__CAPABILITY__CHIP_ENABLE_bw 1 +#define HYPERBUS_CFG_REGS__CAPABILITY__CHIP_ENABLE_reset 0x0 +#define HYPERBUS_CFG_REGS__CAPABILITY__CLOCK_DIVIDER_bm 0x80000 +#define HYPERBUS_CFG_REGS__CAPABILITY__CLOCK_DIVIDER_bp 19 +#define HYPERBUS_CFG_REGS__CAPABILITY__CLOCK_DIVIDER_bw 1 +#define HYPERBUS_CFG_REGS__CAPABILITY__CLOCK_DIVIDER_reset 0x0 +#define HYPERBUS_CFG_REGS__CAPABILITY__STAGED_APPLY_bm 0x100000 +#define HYPERBUS_CFG_REGS__CAPABILITY__STAGED_APPLY_bp 20 +#define HYPERBUS_CFG_REGS__CAPABILITY__STAGED_APPLY_bw 1 +#define HYPERBUS_CFG_REGS__CAPABILITY__STAGED_APPLY_reset 0x0 +#define HYPERBUS_CFG_REGS__CAPABILITY__ERROR_STATUS_bm 0x200000 +#define HYPERBUS_CFG_REGS__CAPABILITY__ERROR_STATUS_bp 21 +#define HYPERBUS_CFG_REGS__CAPABILITY__ERROR_STATUS_bw 1 +#define HYPERBUS_CFG_REGS__CAPABILITY__ERROR_STATUS_reset 0x0 +#define HYPERBUS_CFG_REGS__CAPABILITY__RWDS_SAMPLE_TIMING_bm 0x400000 +#define HYPERBUS_CFG_REGS__CAPABILITY__RWDS_SAMPLE_TIMING_bp 22 +#define HYPERBUS_CFG_REGS__CAPABILITY__RWDS_SAMPLE_TIMING_bw 1 +#define HYPERBUS_CFG_REGS__CAPABILITY__RWDS_SAMPLE_TIMING_reset 0x0 +#define HYPERBUS_CFG_REGS__CAPABILITY__RWDS_OE_TIMING_bm 0x800000 +#define HYPERBUS_CFG_REGS__CAPABILITY__RWDS_OE_TIMING_bp 23 +#define HYPERBUS_CFG_REGS__CAPABILITY__RWDS_OE_TIMING_bw 1 +#define HYPERBUS_CFG_REGS__CAPABILITY__RWDS_OE_TIMING_reset 0x0 + +// reg - hyperbus_cfg_regs::command +#define HYPERBUS_CFG_REGS__COMMAND__FLUSH_bm 0x1 +#define HYPERBUS_CFG_REGS__COMMAND__FLUSH_bp 0 +#define HYPERBUS_CFG_REGS__COMMAND__FLUSH_bw 1 +#define HYPERBUS_CFG_REGS__COMMAND__FLUSH_reset 0x0 +#define HYPERBUS_CFG_REGS__COMMAND__APPLY_bm 0x2 +#define HYPERBUS_CFG_REGS__COMMAND__APPLY_bp 1 +#define HYPERBUS_CFG_REGS__COMMAND__APPLY_bw 1 +#define HYPERBUS_CFG_REGS__COMMAND__APPLY_reset 0x0 + +// 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 +#define HYPERBUS_CFG_REGS__STATUS__BUSY_bm 0x2 +#define HYPERBUS_CFG_REGS__STATUS__BUSY_bp 1 +#define HYPERBUS_CFG_REGS__STATUS__BUSY_bw 1 +#define HYPERBUS_CFG_REGS__STATUS__BUSY_reset 0x0 +#define HYPERBUS_CFG_REGS__STATUS__DIRTY_bm 0x4 +#define HYPERBUS_CFG_REGS__STATUS__DIRTY_bp 2 +#define HYPERBUS_CFG_REGS__STATUS__DIRTY_bw 1 +#define HYPERBUS_CFG_REGS__STATUS__DIRTY_reset 0x0 + +// regfile - hyperbus_cfg_regs::global_regs +typedef struct __attribute__ ((__packed__)) { + uint32_t ip_version; + uint32_t reg_if_version; + uint32_t capability; + uint32_t command; + uint32_t status; +} hyperbus_cfg_regs__global_regs_t; + +// reg - hyperbus_cfg_regs::frontend_cfg +#define HYPERBUS_CFG_REGS__FRONTEND_CFG__DUAL_PHY_bm 0x1 +#define HYPERBUS_CFG_REGS__FRONTEND_CFG__DUAL_PHY_bp 0 +#define HYPERBUS_CFG_REGS__FRONTEND_CFG__DUAL_PHY_bw 1 +#define HYPERBUS_CFG_REGS__FRONTEND_CFG__DUAL_PHY_reset 0x1 + +// regfile - hyperbus_cfg_regs::frontend_regs +typedef struct __attribute__ ((__packed__)) { + uint32_t frontend_cfg; +} hyperbus_cfg_regs__frontend_regs_t; + +// reg - hyperbus_cfg_regs::clock_cfg +#define HYPERBUS_CFG_REGS__CLOCK_CFG__DIVIDER_bm 0xff +#define HYPERBUS_CFG_REGS__CLOCK_CFG__DIVIDER_bp 0 +#define HYPERBUS_CFG_REGS__CLOCK_CFG__DIVIDER_bw 8 +#define HYPERBUS_CFG_REGS__CLOCK_CFG__DIVIDER_reset 0x8 + +// regfile - hyperbus_cfg_regs::backend_regs +typedef struct __attribute__ ((__packed__)) { + uint32_t clock_cfg; +} hyperbus_cfg_regs__backend_regs_t; + +// reg - hyperbus_cfg_regs::tx_delay +#define HYPERBUS_CFG_REGS__TX_DELAY__VALUE_bm 0xff +#define HYPERBUS_CFG_REGS__TX_DELAY__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__TX_DELAY__VALUE_bw 8 +#define HYPERBUS_CFG_REGS__TX_DELAY__VALUE_reset 0x10 + +// reg - hyperbus_cfg_regs::rwds_timing +#define HYPERBUS_CFG_REGS__RWDS_TIMING__RWDS_OE_SETUP_CYCLES_bm 0xff +#define HYPERBUS_CFG_REGS__RWDS_TIMING__RWDS_OE_SETUP_CYCLES_bp 0 +#define HYPERBUS_CFG_REGS__RWDS_TIMING__RWDS_OE_SETUP_CYCLES_bw 8 +#define HYPERBUS_CFG_REGS__RWDS_TIMING__RWDS_OE_SETUP_CYCLES_reset 0x1 + +// regfile - hyperbus_cfg_regs::phy_regs +typedef struct __attribute__ ((__packed__)) { + uint32_t tx_delay; + uint32_t rwds_timing; +} hyperbus_cfg_regs__phy_regs_t; + +// reg - hyperbus_cfg_regs::range_base +#define HYPERBUS_CFG_REGS__RANGE_BASE__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BASE__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BASE__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BASE__VALUE_reset 0x0 + +// reg - hyperbus_cfg_regs::range_bound +#define HYPERBUS_CFG_REGS__RANGE_BOUND__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BOUND__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BOUND__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BOUND__VALUE_reset 0x1 + +// reg - hyperbus_cfg_regs::address_cfg +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ADDRESS_SPACE_bm 0xff +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ADDRESS_SPACE_bp 0 +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ADDRESS_SPACE_bw 8 +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ADDRESS_SPACE_reset 0x0 +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ADDRESS_MASK_MSB_bm 0xff00 +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ADDRESS_MASK_MSB_bp 8 +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ADDRESS_MASK_MSB_bw 8 +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ADDRESS_MASK_MSB_reset 0x19 +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ENABLE_bm 0x10000 +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ENABLE_bp 16 +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ENABLE_bw 1 +#define HYPERBUS_CFG_REGS__ADDRESS_CFG__ENABLE_reset 0x1 + +// reg - hyperbus_cfg_regs::latency_cfg +#define HYPERBUS_CFG_REGS__LATENCY_CFG__T_LATENCY_ACCESS_bm 0xff +#define HYPERBUS_CFG_REGS__LATENCY_CFG__T_LATENCY_ACCESS_bp 0 +#define HYPERBUS_CFG_REGS__LATENCY_CFG__T_LATENCY_ACCESS_bw 8 +#define HYPERBUS_CFG_REGS__LATENCY_CFG__T_LATENCY_ACCESS_reset 0x6 +#define HYPERBUS_CFG_REGS__LATENCY_CFG__RWDS_SAMPLE_DELAY_bm 0xff00 +#define HYPERBUS_CFG_REGS__LATENCY_CFG__RWDS_SAMPLE_DELAY_bp 8 +#define HYPERBUS_CFG_REGS__LATENCY_CFG__RWDS_SAMPLE_DELAY_bw 8 +#define HYPERBUS_CFG_REGS__LATENCY_CFG__RWDS_SAMPLE_DELAY_reset 0x0 +#define HYPERBUS_CFG_REGS__LATENCY_CFG__EN_LATENCY_ADDITIONAL_bm 0x10000 +#define HYPERBUS_CFG_REGS__LATENCY_CFG__EN_LATENCY_ADDITIONAL_bp 16 +#define HYPERBUS_CFG_REGS__LATENCY_CFG__EN_LATENCY_ADDITIONAL_bw 1 +#define HYPERBUS_CFG_REGS__LATENCY_CFG__EN_LATENCY_ADDITIONAL_reset 0x0 + +// reg - hyperbus_cfg_regs::burst_cfg +#define HYPERBUS_CFG_REGS__BURST_CFG__T_BURST_MAX_bm 0xffff +#define HYPERBUS_CFG_REGS__BURST_CFG__T_BURST_MAX_bp 0 +#define HYPERBUS_CFG_REGS__BURST_CFG__T_BURST_MAX_bw 16 +#define HYPERBUS_CFG_REGS__BURST_CFG__T_BURST_MAX_reset 0x15e + +// reg - hyperbus_cfg_regs::chip_timing +#define HYPERBUS_CFG_REGS__CHIP_TIMING__T_READ_WRITE_RECOVERY_bm 0xff +#define HYPERBUS_CFG_REGS__CHIP_TIMING__T_READ_WRITE_RECOVERY_bp 0 +#define HYPERBUS_CFG_REGS__CHIP_TIMING__T_READ_WRITE_RECOVERY_bw 8 +#define HYPERBUS_CFG_REGS__CHIP_TIMING__T_READ_WRITE_RECOVERY_reset 0x6 +#define HYPERBUS_CFG_REGS__CHIP_TIMING__T_CSH_CYCLES_bm 0xff00 +#define HYPERBUS_CFG_REGS__CHIP_TIMING__T_CSH_CYCLES_bp 8 +#define HYPERBUS_CFG_REGS__CHIP_TIMING__T_CSH_CYCLES_bw 8 +#define HYPERBUS_CFG_REGS__CHIP_TIMING__T_CSH_CYCLES_reset 0x1 +#define HYPERBUS_CFG_REGS__CHIP_TIMING__CSN_TO_CK_CYCLES_bm 0xff0000 +#define HYPERBUS_CFG_REGS__CHIP_TIMING__CSN_TO_CK_CYCLES_bp 16 +#define HYPERBUS_CFG_REGS__CHIP_TIMING__CSN_TO_CK_CYCLES_bw 8 +#define HYPERBUS_CFG_REGS__CHIP_TIMING__CSN_TO_CK_CYCLES_reset 0x0 + +// reg - hyperbus_cfg_regs::rx_delay +#define HYPERBUS_CFG_REGS__RX_DELAY__VALUE_bm 0xff +#define HYPERBUS_CFG_REGS__RX_DELAY__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__RX_DELAY__VALUE_bw 8 +#define HYPERBUS_CFG_REGS__RX_DELAY__VALUE_reset 0x10 + +// regfile - hyperbus_cfg_regs::chip_regs +typedef struct __attribute__ ((__packed__)) { + uint32_t range_base; + uint32_t range_bound; + uint32_t address_cfg; + uint32_t latency_cfg; + uint32_t burst_cfg; + uint32_t chip_timing; + uint32_t rx_delay; +} hyperbus_cfg_regs__chip_regs_t; + +// reg - hyperbus_cfg_regs::range_base_value_aea1d0e1 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_AEA1D0E1__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_AEA1D0E1__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_AEA1D0E1__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_AEA1D0E1__VALUE_reset 0x1 + +// reg - hyperbus_cfg_regs::range_bound_value_e30f0c3f +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_E30F0C3F__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_E30F0C3F__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_E30F0C3F__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_E30F0C3F__VALUE_reset 0x2 + +// regfile - hyperbus_cfg_regs::chip_regs_range_base_de2a4636_range_bound_d40fd139 +typedef struct __attribute__ ((__packed__)) { + uint32_t range_base; + uint32_t range_bound; + uint32_t address_cfg; + uint32_t latency_cfg; + uint32_t burst_cfg; + uint32_t chip_timing; + uint32_t rx_delay; +} hyperbus_cfg_regs__chip_regs_range_base_de2a4636_range_bound_d40fd139_t; + +// reg - hyperbus_cfg_regs::range_base_value_e30f0c3f +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_E30F0C3F__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_E30F0C3F__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_E30F0C3F__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_E30F0C3F__VALUE_reset 0x2 + +// reg - hyperbus_cfg_regs::range_bound_value_2f629ec9 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_2F629EC9__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_2F629EC9__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_2F629EC9__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_2F629EC9__VALUE_reset 0x3 + +// regfile - hyperbus_cfg_regs::chip_regs_range_base_b054dd4c_range_bound_a4d5f83b +typedef struct __attribute__ ((__packed__)) { + uint32_t range_base; + uint32_t range_bound; + uint32_t address_cfg; + uint32_t latency_cfg; + uint32_t burst_cfg; + uint32_t chip_timing; + uint32_t rx_delay; +} hyperbus_cfg_regs__chip_regs_range_base_b054dd4c_range_bound_a4d5f83b_t; + +// reg - hyperbus_cfg_regs::range_base_value_2f629ec9 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_2F629EC9__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_2F629EC9__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_2F629EC9__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_2F629EC9__VALUE_reset 0x3 + +// reg - hyperbus_cfg_regs::range_bound_value_a1e733c2 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_A1E733C2__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_A1E733C2__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_A1E733C2__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_A1E733C2__VALUE_reset 0x4 + +// regfile - hyperbus_cfg_regs::chip_regs_range_base_f7266ded_range_bound_5bf260e9 +typedef struct __attribute__ ((__packed__)) { + uint32_t range_base; + uint32_t range_bound; + uint32_t address_cfg; + uint32_t latency_cfg; + uint32_t burst_cfg; + uint32_t chip_timing; + uint32_t rx_delay; +} hyperbus_cfg_regs__chip_regs_range_base_f7266ded_range_bound_5bf260e9_t; + +// reg - hyperbus_cfg_regs::range_base_value_a1e733c2 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_A1E733C2__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_A1E733C2__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_A1E733C2__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_A1E733C2__VALUE_reset 0x4 + +// reg - hyperbus_cfg_regs::range_bound_value_342915db +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_342915DB__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_342915DB__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_342915DB__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_342915DB__VALUE_reset 0x5 + +// regfile - hyperbus_cfg_regs::chip_regs_range_base_0d543b99_range_bound_1fffc3a2 +typedef struct __attribute__ ((__packed__)) { + uint32_t range_base; + uint32_t range_bound; + uint32_t address_cfg; + uint32_t latency_cfg; + uint32_t burst_cfg; + uint32_t chip_timing; + uint32_t rx_delay; +} hyperbus_cfg_regs__chip_regs_range_base_0d543b99_range_bound_1fffc3a2_t; + +// reg - hyperbus_cfg_regs::range_base_value_342915db +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_342915DB__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_342915DB__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_342915DB__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_342915DB__VALUE_reset 0x5 + +// reg - hyperbus_cfg_regs::range_bound_value_883c384f +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_883C384F__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_883C384F__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_883C384F__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_883C384F__VALUE_reset 0x6 + +// regfile - hyperbus_cfg_regs::chip_regs_range_base_e068ab2e_range_bound_9afa5911 +typedef struct __attribute__ ((__packed__)) { + uint32_t range_base; + uint32_t range_bound; + uint32_t address_cfg; + uint32_t latency_cfg; + uint32_t burst_cfg; + uint32_t chip_timing; + uint32_t rx_delay; +} hyperbus_cfg_regs__chip_regs_range_base_e068ab2e_range_bound_9afa5911_t; + +// reg - hyperbus_cfg_regs::range_base_value_883c384f +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_883C384F__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_883C384F__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_883C384F__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_883C384F__VALUE_reset 0x6 + +// reg - hyperbus_cfg_regs::range_bound_value_d0d03147 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_D0D03147__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_D0D03147__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_D0D03147__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_D0D03147__VALUE_reset 0x7 + +// regfile - hyperbus_cfg_regs::chip_regs_range_base_ac4fb754_range_bound_24b9644b +typedef struct __attribute__ ((__packed__)) { + uint32_t range_base; + uint32_t range_bound; + uint32_t address_cfg; + uint32_t latency_cfg; + uint32_t burst_cfg; + uint32_t chip_timing; + uint32_t rx_delay; +} hyperbus_cfg_regs__chip_regs_range_base_ac4fb754_range_bound_24b9644b_t; + +// reg - hyperbus_cfg_regs::range_base_value_d0d03147 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_D0D03147__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_D0D03147__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_D0D03147__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BASE_VALUE_D0D03147__VALUE_reset 0x7 + +// reg - hyperbus_cfg_regs::range_bound_value_971bab53 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_971BAB53__VALUE_bm 0xffc00000 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_971BAB53__VALUE_bp 22 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_971BAB53__VALUE_bw 10 +#define HYPERBUS_CFG_REGS__RANGE_BOUND_VALUE_971BAB53__VALUE_reset 0x8 + +// regfile - hyperbus_cfg_regs::chip_regs_range_base_63c2ba90_range_bound_602aea67 +typedef struct __attribute__ ((__packed__)) { + uint32_t range_base; + uint32_t range_bound; + uint32_t address_cfg; + uint32_t latency_cfg; + uint32_t burst_cfg; + uint32_t chip_timing; + uint32_t rx_delay; +} hyperbus_cfg_regs__chip_regs_range_base_63c2ba90_range_bound_602aea67_t; + +// reg - hyperbus_cfg_regs::reserved_top +#define HYPERBUS_CFG_REGS__RESERVED_TOP__VALUE_bm 0xffffffff +#define HYPERBUS_CFG_REGS__RESERVED_TOP__VALUE_bp 0 +#define HYPERBUS_CFG_REGS__RESERVED_TOP__VALUE_bw 32 +#define HYPERBUS_CFG_REGS__RESERVED_TOP__VALUE_reset 0x0 + +// addrmap - hyperbus_cfg_regs +typedef struct __attribute__ ((__packed__)) { + hyperbus_cfg_regs__global_regs_t global_cfg; + uint8_t RESERVED_14_ff[0xec]; + hyperbus_cfg_regs__frontend_regs_t frontend; + uint8_t RESERVED_104_1ff[0xfc]; + hyperbus_cfg_regs__backend_regs_t backend; + uint8_t RESERVED_204_2ff[0xfc]; + hyperbus_cfg_regs__phy_regs_t phy_0; + uint8_t RESERVED_308_33f[0x38]; + hyperbus_cfg_regs__phy_regs_t phy_1; + uint8_t RESERVED_348_3ff[0xb8]; + hyperbus_cfg_regs__chip_regs_t chip_0; + uint8_t RESERVED_41c_43f[0x24]; + hyperbus_cfg_regs__chip_regs_range_base_de2a4636_range_bound_d40fd139_t chip_1; + uint8_t RESERVED_45c_47f[0x24]; + hyperbus_cfg_regs__chip_regs_range_base_b054dd4c_range_bound_a4d5f83b_t chip_2; + uint8_t RESERVED_49c_4bf[0x24]; + hyperbus_cfg_regs__chip_regs_range_base_f7266ded_range_bound_5bf260e9_t chip_3; + uint8_t RESERVED_4dc_4ff[0x24]; + hyperbus_cfg_regs__chip_regs_range_base_0d543b99_range_bound_1fffc3a2_t chip_4; + uint8_t RESERVED_51c_53f[0x24]; + hyperbus_cfg_regs__chip_regs_range_base_e068ab2e_range_bound_9afa5911_t chip_5; + uint8_t RESERVED_55c_57f[0x24]; + hyperbus_cfg_regs__chip_regs_range_base_ac4fb754_range_bound_24b9644b_t chip_6; + uint8_t RESERVED_59c_5bf[0x24]; + hyperbus_cfg_regs__chip_regs_range_base_63c2ba90_range_bound_602aea67_t chip_7; + uint8_t RESERVED_5dc_ffb[0xa20]; + uint32_t reserved_top; +} hyperbus_cfg_regs_t; + + +static_assert(sizeof(hyperbus_cfg_regs_t) == 0x1000, "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..8ceb003 --- /dev/null +++ b/scripts/gen_regs.sh @@ -0,0 +1,48 @@ +#!/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" +PEAKRDL_ARGS=( + --from "peakrdl-cli==1.5.0" + --with "peakrdl-regblock==1.3.1" + --with "peakrdl-cheader==1.1.0" + --with "peakrdl-markdown==1.0.3" +) + +mkdir -p "${ROOT}/include" "${ROOT}/docs/regs" +rm -rf "${ROOT}/docs/regs/hyperbus_cfg_regs" + +uvx "${PEAKRDL_ARGS[@]}" \ + peakrdl regblock \ + --cpuif apb4-flat \ + --default-reset arst_n \ + --module-name hyperbus_cfg_regblock \ + --package-name hyperbus_cfg_regblock_pkg \ + --addr-width 12 \ + -o "${ROOT}/src/regs" \ + "${RDL}" + +uvx "${PEAKRDL_ARGS[@]}" \ + peakrdl c-header \ + -o "${ROOT}/include/hyperbus_cfg_regs.h" \ + "${RDL}" + +uvx "${PEAKRDL_ARGS[@]}" \ + peakrdl markdown \ + -o "${ROOT}/docs/regs/hyperbus_cfg_regs.md" \ + "${RDL}" + +# Keep generated RTL compatible with the repository whitespace checks. +sed -i 's/[[:space:]]\+$//' \ + "${ROOT}/src/regs/hyperbus_cfg_regblock.sv" \ + "${ROOT}/src/regs/hyperbus_cfg_regblock_pkg.sv" diff --git a/scripts/start.tcl b/scripts/start.tcl index 0488812..7d64da1 100644 --- a/scripts/start.tcl +++ b/scripts/start.tcl @@ -2,12 +2,47 @@ # 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 + + # 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 + 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_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 +run_test axi_pre_aw_drain_tb sim_run_pre_aw_drain.wlf + +quit -code $regression_failed -f diff --git a/src/backend/hyperbus_backend.sv b/src/backend/hyperbus_backend.sv new file mode 100644 index 0000000..ac3e4d2 --- /dev/null +++ b/src/backend/hyperbus_backend.sv @@ -0,0 +1,259 @@ +// 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/assertions.svh" +`include "common_cells/registers.svh" + +module hyperbus_backend #( + 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 hyper_req_t = logic, + parameter type hyper_rsp_t = logic +) ( + input logic clk_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, + input logic cfg_apply_valid_i, + output logic cfg_apply_ready_o, + + output logic busy_o, + + input hyper_req_t req_i, + output hyper_rsp_t rsp_o, + + 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, + 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 +); + + ///////////////////////////// + // Configuration and clock // + ///////////////////////////// + + hyperbus_pkg::phy_cfg_t cfg_q; + logic cfg_apply_accepted; + logic phy_busy_any; + logic clk_tx; + + `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) + `ASSERT_INIT(SyncStagesValid, SyncStages >= 2) + + 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; + + `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 ) + ); + + ///////////////////// + // 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; + 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 #( + .StartupCycles ( StartupCycles ), + .NumPhys ( NumPhys ), + .SyncStages ( SyncStages ) + ) i_phy ( + .clk_i, + .clk_tx_i ( clk_tx ), + .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 #( + .StartupCycles ( StartupCycles ), + .NumPhys ( NumPhys ), + .SyncStages ( SyncStages ) + ) i_phy ( + .clk_i, + .clk_tx_i ( clk_tx ), + .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_clock_diff_out.sv b/src/backend/hyperbus_clock_diff_out.sv similarity index 69% rename from src/hyperbus_clock_diff_out.sv rename to src/backend/hyperbus_clock_diff_out.sv index 67c6ae6..72ce9fc 100644 --- a/src/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/hyperbus_ddr_out.sv b/src/backend/hyperbus_ddr_out.sv similarity index 84% rename from src/hyperbus_ddr_out.sv rename to src/backend/hyperbus_ddr_out.sv index d737d15..38f57f5 100644 --- a/src/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/hyperbus_delay.sv b/src/backend/hyperbus_delay.sv similarity index 97% rename from src/hyperbus_delay.sv rename to src/backend/hyperbus_delay.sv index 00ea849..43dffc8 100644 --- a/src/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/hyperbus_phy.sv b/src/backend/hyperbus_phy.sv similarity index 76% rename from src/hyperbus_phy.sv rename to src/backend/hyperbus_phy.sv index 43a5a8d..031a830 100644 --- a/src/hyperbus_phy.sv +++ b/src/backend/hyperbus_phy.sv @@ -10,26 +10,26 @@ `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_i_90, + input logic clk_tx_i, 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 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, @@ -59,15 +59,26 @@ module hyperbus_phy import hyperbus_pkg::*; #( output logic hyper_reset_no ); - logic [1:0] phys_in_use; + localparam int unsigned RxFifoDepth = 2 ** RxFifoLogDepth; + // 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; - assign phys_in_use = (NumPhys==2) ? (cfg_i.phys_in_use + 1) : 1; + logic [1:0] words_per_beat; + + assign words_per_beat = (NumPhys == 2 && cfg_i.dual_phy) ? 2 : 1; + + ////////////////////// + // Persistent state // + ////////////////////// - // PHY 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 @@ -80,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; @@ -92,11 +103,12 @@ 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; - // Transciever IO + // Transceiver I/O logic trx_clk_ena; logic trx_cs_ena; logic trx_rwds_sample; @@ -111,17 +123,16 @@ 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 ( .clk_i, - .clk_i_90, + .clk_tx_i, .rst_ni, .test_mode_i, .cs_i ( cs_q ), @@ -133,7 +144,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 ), @@ -151,9 +162,9 @@ module hyperbus_phy import hyperbus_pkg::*; #( .hyper_reset_no ); - // ============== - // Dataflow - // ============== + ////////////// + // Dataflow // + ////////////// // Command-address assign ca = hyper_phy_ca_t '{ @@ -201,10 +212,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 @@ -215,16 +228,16 @@ 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; - // 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_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); @@ -254,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 @@ -262,21 +276,23 @@ 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; - // 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; - 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; @@ -290,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; @@ -298,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; @@ -305,15 +323,16 @@ 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 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; @@ -325,7 +344,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,17 +364,18 @@ 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 + // Complete the requested second access-latency interval. WaitAddLatAccess: begin // Same as WaitLatAccess but without possibility // of adding another latency count 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; @@ -367,25 +387,27 @@ 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; 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.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 + // 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; @@ -394,33 +416,39 @@ 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; - 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 + // 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 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 + // 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 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 @@ -429,12 +457,21 @@ 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 // 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_trx.sv b/src/backend/hyperbus_trx.sv similarity index 73% rename from src/hyperbus_trx.sv rename to src/backend/hyperbus_trx.sv index cc4c79b..d0b882e 100644 --- a/src/hyperbus_trx.sv +++ b/src/backend/hyperbus_trx.sv @@ -6,19 +6,20 @@ // Armin Berger // Stephan Keck +`include "common_cells/assertions.svh" + module hyperbus_trx #( - parameter int unsigned NumChips = 2, parameter int unsigned RxFifoLogDepth = 3, parameter int unsigned SyncStages = 2 )( // 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, // 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, @@ -36,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, @@ -48,37 +49,19 @@ 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 - - // Intermediate RX signals for RWDS domain - logic rx_rwds_clk_ena; - logic rx_rwds_clk_orig; - logic rx_rwds_clk; - logic rx_rwds_soft_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 // + ////////////////// - // Shift clock by 90 degrees - assign tx_clk_90 = clk_i_90; + logic tx_clk_ena_q; - // 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 ) @@ -86,7 +69,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 +100,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; @@ -128,19 +111,29 @@ 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 - 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 +141,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 ), @@ -172,14 +165,14 @@ 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? - assign rx_rwds_soft_rst = ~rst_ni | (~rx_rwds_clk_ena & ~test_mode_i); + // 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 - 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 +190,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 ( @@ -227,10 +220,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"); - `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/backend/hyperbus_tx_clk_delay.sv b/src/backend/hyperbus_tx_clk_delay.sv new file mode 100644 index 0000000..05c4393 --- /dev/null +++ b/src/backend/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/src/hyperbus.sv b/src/hyperbus.sv deleted file mode 100644 index da287fa..0000000 --- a/src/hyperbus.sv +++ /dev/null @@ -1,314 +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 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 -) ( - 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::hyper_cfg_t 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 ), - .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_sys_i ), - .rst_ni ( rst_sys_ni ), - .reg_req_i ( reg_req_i ), - .reg_rsp_o ( reg_rsp_o ), - .cfg_o ( 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 ), - - .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 ) - ); - - 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 ( 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..e2644c2 --- /dev/null +++ b/src/hyperbus_async_bridge.sv @@ -0,0 +1,186 @@ +// 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/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 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, + input logic clk_phy_i, + input logic rst_phy_ni, + + 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, + + 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 +); + + //////////////////////////// + // Configuration crossing // + //////////////////////////// + + logic cfg_apply_accepted; + 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_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 : proc_cfg_apply_pending + cfg_apply_pending_d = cfg_apply_pending_q; + + 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 + + `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 ) + ); + + ////////////////////// + // Command crossing // + ////////////////////// + + 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_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 ) + ); + + ///////////////////////////// + // Write-response crossing // + ///////////////////////////// + + cdc_2phase_clearable #( + .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_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 ) + ); + + /////////////////////// + // TX data crossing // + /////////////////////// + + 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_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 ) + ); + + /////////////////////// + // RX data crossing // + /////////////////////// + + 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_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 new file mode 100644 index 0000000..0c0d567 --- /dev/null +++ b/src/hyperbus_asynchronous.sv @@ -0,0 +1,285 @@ +// 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 "hyperbus/typedef.svh" +`include "common_cells/assertions.svh" + +module hyperbus_asynchronous #( + 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 AxiMaxReadTxns = 4, + parameter int unsigned AxiMaxWriteTxns = 4, + parameter int unsigned HostWriteBufferBytes = 64, + 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, + 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][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, + 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 +); + + `ASSERT_INIT(AxiAddrWidthValid, AxiAddrWidth >= $clog2(AxiDataWidth / 8)) + `ASSERT_INIT(AxiMaxReadTxnsValid, AxiMaxReadTxns >= 1) + `ASSERT_INIT(AxiMaxWriteTxnsValid, AxiMaxWriteTxns >= 1) + + 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; + localparam int unsigned AxiDataBytes = AxiDataWidth / 8; + `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) + `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys) + + ///////////////////// + // Clock and reset // + ///////////////////// + + logic clk_backend; + logic rst_backend_n; + logic rst_phy_async_n; + + //////////////////////// + // Configuration path // + //////////////////////// + + 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; + logic backend_cfg_apply_valid; + logic backend_cfg_apply_ready; + + hyperbus_pkg::frontend_cfg_t frontend_cfg; + axi_rule_t [hyperbus_pkg::HyperNumChips-1:0] frontend_chip_rules; + + //////////////////// + // Dataflow links // + //////////////////// + + host_req_t host_req; + host_rsp_t host_rsp; + hyper_req_t midend_req; + hyper_rsp_t midend_rsp; + logic midend_trans_active; + logic midend_decode_error; + hyper_req_t backend_req; + hyper_rsp_t backend_rsp; + + // 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; + + 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 ( ) + ); + + //////////////////////////// + // Configuration frontend // + //////////////////////////// + + hyperbus_cfg_frontend #( + .NumPhys ( NumPhys ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .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 ), + .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 ) + ); + + ////////////////// + // AXI frontend // + ////////////////// + + hyperbus_axi_frontend #( + .AxiDataWidth ( AxiDataWidth ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .AxiMaxReadTxns ( AxiMaxReadTxns ), + .AxiMaxWriteTxns ( AxiMaxWriteTxns ), + .MaxWriteDataBeats ( HostWriteBufferBytes / AxiDataBytes ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .host_req_t ( host_req_t ), + .host_rsp_t ( host_rsp_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_rsp_i ( host_rsp ) + ); + + //////////// + // Midend // + //////////// + + hyperbus_midend #( + .HostAddrWidth ( AxiAddrWidth ), + .HostDataWidth ( AxiDataWidth ), + .NumPhys ( NumPhys ), + .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 ), + .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 ), + .hyper_link_req_o ( midend_req ), + .hyper_link_rsp_i ( midend_rsp ) + ); + + ///////////////////////// + // Asynchronous bridge // + ///////////////////////// + + hyperbus_async_bridge #( + .RxFifoLogDepth ( RxFifoLogDepth ), + .TxFifoLogDepth ( TxFifoLogDepth ), + .CdcSyncStages ( CdcSyncStages ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_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 ), + .clk_phy_i ( clk_backend ), + .rst_phy_ni ( rst_backend_n ), + .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_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 ) + ); + + ///////////// + // Backend // + ///////////// + + hyperbus_backend #( + .NumPhys ( NumPhys ), + .StartupCycles ( PhyStartupCycles ), + .SyncStages ( SyncStages ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ) + ) i_backend ( + .clk_i ( clk_backend ), + .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 ( ), + .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 ), + .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 ) + ); + + `ASSERT(PhyResetOnlyWhenIdle, !rst_phy_ni |-> !midend_trans_active, + clk_sys_i, !rst_sys_ni) + +endmodule diff --git a/src/hyperbus_atomic_handler.sv b/src/hyperbus_atomic_handler.sv new file mode 100644 index 0000000..b301199 --- /dev/null +++ b/src/hyperbus_atomic_handler.sv @@ -0,0 +1,369 @@ +// 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" + +/// 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, + 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; + + ////////////////////// + // Persistent state // + ////////////////////// + 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; + + //////////////////////// + // 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); + + ////////////////////// + // 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.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; + end + + //////////////// + // 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; + + 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; + value_mask = value_mask >> (HostDataWidth - operand_bits); + end + + 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: 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 + 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; + 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 + + /////////////////////////// + // 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; + 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) + // Capture one validated or rejected atomic request. + Idle: begin + 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 (operand_accepted) begin + operand_d = host_w_i.data; + 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 + // 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 (backend_read_accepted) begin + read_data_d = read_i.data; + 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 + // 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 (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; + end + end + // Hold the host read and write responses until independently accepted. + ReturnResponse: begin + 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) + `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 deleted file mode 100644 index 78b3c7d..0000000 --- a/src/hyperbus_axi.sv +++ /dev/null @@ -1,556 +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/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 -) ( - 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 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, - output logic trans_active_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); - - 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; - - `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]) - - // No need to track ID: serializer buffers it for us - typedef struct packed { - axi_addr_t addr; - axi_pkg::len_t len; - axi_pkg::burst_t burst; - axi_pkg::size_t size; - } axi_ax_t; - - typedef struct packed { - 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_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_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; - - // 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; - - logic [1:0] phys_in_use; - - assign phys_in_use = (NumPhys==2) ? (phys_in_use_i + 1) : 1; - - // ============================ - // Serialize requests - // ============================ - - 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 ( 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 ) - ); - - // 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 ) - ) 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 ) - ); - - // 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_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; - - rr_arb_tree #( - .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 ) - ); - - // 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; - - 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 = addr_space_i; - 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); - - // 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) & (~phys_in_use_i) ) begin - if(rx_valid_i & s_rx_ready) begin - merge_r_d = merge_r_q + 1; - end - if(~which_phy_i) - 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(~which_phy_i) - 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 ( - .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 ) - ); - - `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) - - // ========================================================= - // W channel: Buffer. Cuts path and upsamples when needed - // ========================================================= - - 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; - - stream_fifo #( - .FALL_THROUGH ( 1'b0 ), - .T ( axi_w_chan_t ), - .DEPTH ( 16 ) - ) 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 ) - ); - - 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) & (~phys_in_use_i) ) 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 = (rx_valid_i & rx_ready_o & rx_i.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 - - // ========================= - // 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; - end - end - - // ========================= - // Assertions - // ========================= - - // 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."); - `endif - // pragma translate_on - -endmodule diff --git a/src/hyperbus_axi_frontend.sv b/src/hyperbus_axi_frontend.sv new file mode 100644 index 0000000..c110ec2 --- /dev/null +++ b/src/hyperbus_axi_frontend.sv @@ -0,0 +1,379 @@ +// 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 "axi/typedef.svh" +`include "common_cells/registers.svh" +`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 int unsigned AxiMaxReadTxns = 4, + parameter int unsigned AxiMaxWriteTxns = 4, + // Maximum number of independently accepted W beats held by the host path. + parameter int unsigned MaxWriteDataBeats = 1, + parameter type axi_req_t = logic, + parameter type axi_rsp_t = logic, + parameter type host_req_t = logic, + parameter type host_rsp_t = logic +) ( + 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, + input host_rsp_t host_rsp_i +); + + `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) + `ASSERT_INIT(AxiMaxReadTxnsValid, AxiMaxReadTxns >= 1) + `ASSERT_INIT(AxiMaxWriteTxnsValid, AxiMaxWriteTxns >= 1) + `ASSERT_INIT(MaxWriteDataBeatsValid, MaxWriteDataBeats >= 1) + + typedef logic [AxiAddrWidth-1:0] axi_addr_t; + + // 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_pkg::atop_t atop; + } axi_ax_t; + + typedef struct packed { + axi_ax_t ax_data; + logic write; + } ax_channel_t; + + /////////////////////////// + // 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; + 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; + + logic ax_arb_valid; + logic ax_arb_ready; + + ////////////////////// + // Drain accounting // + ////////////////////// + + // Widths cover the configured serializer queues. ATOP read responses are + // admitted only after the serializer has drained the existing ID FIFOs. + localparam longint unsigned AxiMaxReadTxnsLong = AxiMaxReadTxns; + localparam longint unsigned AxiMaxWriteTxnsLong = AxiMaxWriteTxns; + localparam int unsigned ReadPendingWidth = (AxiMaxReadTxns < 1) ? + 1 : $clog2(AxiMaxReadTxnsLong + 64'd1); + localparam int unsigned WritePendingWidth = (AxiMaxWriteTxns < 1) ? + 1 : $clog2(AxiMaxWriteTxnsLong + 64'd1); + // The balance can be positive for AWs accepted ahead of W, or negative for + // complete W bursts buffered ahead of their matching AW. Use the larger magnitude; + // the 64-bit literal keeps the width calculation from overflowing when a + // 32-bit parameter is at its maximum value. + localparam longint unsigned MaxWriteDataBeatsLong = MaxWriteDataBeats; + localparam int unsigned WriteBalancePositiveBits = + $clog2(AxiMaxWriteTxnsLong + 64'd1); + localparam int unsigned WriteBalanceNegativeBits = + $clog2(MaxWriteDataBeatsLong); + localparam int unsigned WriteBalanceWidth = 1 + + ((WriteBalancePositiveBits > WriteBalanceNegativeBits) ? + WriteBalancePositiveBits : WriteBalanceNegativeBits); + localparam longint unsigned WriteBalanceHalfRange = 64'd1 << (WriteBalanceWidth - 1); + typedef logic [ReadPendingWidth-1:0] read_pending_t; + typedef logic [WritePendingWidth-1:0] write_pending_t; + typedef logic signed [WriteBalanceWidth-1:0] write_balance_t; + + `ASSERT_INIT(WriteBalancePositiveRangeValid, + AxiMaxWriteTxns < WriteBalanceHalfRange) + `ASSERT_INIT(WriteBalanceNegativeRangeValid, + MaxWriteDataBeats <= WriteBalanceHalfRange) + + read_pending_t read_pending_d, read_pending_q; + write_pending_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 allow_aw_for_complete_w, allow_aw_for_partial_w; + logic axi_ar_accepted, axi_aw_accepted, axi_w_accepted; + logic axi_atomic_read_started; + logic axi_r_completed, axi_b_accepted; + + // A negative balance means that one or more complete W bursts are waiting + // for AW. A partial burst with zero balance is also waiting for its AW; + // allowing that AW lets a full W FIFO drain instead of deadlocking. + assign allow_aw_for_complete_w = write_balance_q < 0; + assign allow_aw_for_partial_w = w_partial_q && (write_balance_q == '0); + + 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 = allow_aw_for_complete_w || allow_aw_for_partial_w; + allow_w = w_partial_q || (write_balance_q > 0); + end + + 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 = 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; + 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_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; + write_pending_d = write_pending_q; + write_balance_d = write_balance_q; + w_partial_d = w_partial_q; + + read_pending_d = read_pending_q + read_pending_t'(axi_ar_accepted) + + read_pending_t'(axi_atomic_read_started) - + read_pending_t'(axi_r_completed); + + 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_accepted) begin + write_balance_d = write_balance_d + 1'b1; + end + if (axi_w_accepted && axi_req_i.w.last) begin + write_balance_d = write_balance_d - 1'b1; + end + if (axi_w_accepted) begin + w_partial_d = !axi_req_i.w.last; + end + end + + `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) + + assign idle_o = (read_pending_q == '0) && (write_pending_q == '0) && + (write_balance_q == '0) && !w_partial_q; + + ///////////////////////// + // Command arbitration // + ///////////////////////// + + axi_serializer #( + .MaxReadTxns ( AxiMaxReadTxns ), + .MaxWriteTxns ( AxiMaxWriteTxns ), + .AxiIdWidth ( AxiIdWidth ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_rsp_t ) + ) i_axi_serializer ( + .clk_i, + .rst_ni, + .slv_req_i ( ser_in_req ), + .slv_resp_o ( ser_in_rsp ), + .mst_req_o ( ser_out_req ), + .mst_resp_i ( ser_out_rsp ) + ); + + 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; + + 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 ( ax_channel_t ), + .AxiVldRdy ( 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_channel, ser_out_req_ar_channel} ), + .req_o ( ax_arb_valid ), + .gnt_i ( ax_arb_ready ), + .data_o ( arbitrated_ax ), + .idx_o ( ) + ); + + stream_register #( + .T ( ax_channel_t ) + ) i_ax_register ( + .clk_i, + .rst_ni, + .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 ) + ); + + ////////////////////////// + // Host command mapping // + ////////////////////////// + + 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 = 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 = (selected_ax.ax_data.atop == '0) ? + hyperbus_pkg::HyperAtomicNone : + hyperbus_pkg::HyperAtomicInvalid; + 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 (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: + 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 (atomic_big_endian_unsupported) begin + host_req_o.cmd.atomic_op = hyperbus_pkg::HyperAtomicInvalid; + end + end + end + endcase + 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 + + /////////////////////// + // Host write stream // + /////////////////////// + + 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; + + ////////////////////// + // Response mapping // + ////////////////////// + + 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_rsp_i.r.resp == hyperbus_pkg::HyperRespDecodeError) begin + ser_out_rsp.r.resp = axi_pkg::RESP_DECERR; + end else if (host_rsp_i.r.resp != hyperbus_pkg::HyperRespOkay) begin + ser_out_rsp.r.resp = axi_pkg::RESP_SLVERR; + end + + if (host_rsp_i.wrsp.resp == hyperbus_pkg::HyperRespDecodeError) begin + ser_out_rsp.b.resp = axi_pkg::RESP_DECERR; + 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_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_rsp_i.wrsp_valid; + assign host_req_o.wrsp_ready = ser_out_req.b_ready; + + `ASSERT(AxiBurstType, (host_req_o.cmd_valid && host_rsp_i.cmd_ready) |-> + ((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)) + +endmodule diff --git a/src/hyperbus_cfg_frontend.sv b/src/hyperbus_cfg_frontend.sv new file mode 100644 index 0000000..ce7a07e --- /dev/null +++ b/src/hyperbus_cfg_frontend.sv @@ -0,0 +1,237 @@ +// 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 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, + parameter logic [7:0] CapabilityFeatures = 8'b0010_0000 +) ( + 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 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 [hyperbus_pkg::HyperNumChips-1:0] chip_rules_o, + input logic decode_error_i +); + + typedef enum logic [2:0] { + CfgIdle, + CfgDrain, + CfgCommit, + CfgObserve, + CfgClockSend, + CfgClockWaitAck, + CfgPhySend, + 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; + 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; + + `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) + `ASSERT_INIT(RegDataWidthValid, RegDataWidth == 32) + + ////////////////////////// + // 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; + 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; + 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; + 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 + + ///////////////////////////////// + // 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; + clock_div_applied_d = clock_div_applied_q; + 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 (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 (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_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; + end else if (phy_cfg_changed) begin + cfg_state_d = CfgPhySend; + end else begin + cfg_state_d = CfgIdle; + end + end + // Send a changed divider value to the isochronous clock generator. + CfgClockSend: begin + 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 (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; + cfg_state_d = CfgIdle; + end + end + default: begin + cfg_state_d = CfgIdle; + end + 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 #( + .NumPhys ( NumPhys ), + .RegDataWidth ( RegDataWidth ), + .CapabilityFeatures ( CapabilityFeatures ), + .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 70ac78e..a83979b 100644 --- a/src/hyperbus_cfg_regs.sv +++ b/src/hyperbus_cfg_regs.sv @@ -4,133 +4,302 @@ // // Paul Scheffler +`include "common_cells/assertions.svh" + 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 int unsigned RegAddrWidth = 32, + // Capability bits are supplied by the selected top-level clocking + // implementation. The register map itself remains stable across tops. + parameter logic [7:0] CapabilityFeatures = 8'b0010_0000, 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 addr_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, - output hyperbus_pkg::hyper_cfg_t cfg_o, - output rule_t [NumChips-1:0] chip_rules_o, - input trans_active_i + output hyperbus_pkg::frontend_cfg_t frontend_cfg_o, + output hyperbus_pkg::phy_cfg_t phy_cfg_o, + output addr_rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_o, + input logic decode_error_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]; + `ASSERT_INIT(NumPhysValid, NumPhys == 1 || NumPhys == 2) + `ASSERT_INIT(RegAddrWidthValid, RegAddrWidth >= 12) + `ASSERT_INIT(RegDataWidthValid, RegDataWidth == 32) + + typedef logic [11: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; + hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__in_t cfg_hwif_in; + addr_rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_all; + + cfg_addr_t cfg_addr; + logic cfg_addr_in_window; + logic sel_reg_mapped; + logic cfg_access_active_d, cfg_access_active_q; + logic cfg_access_open; + logic cfg_status_access; + logic cfg_value_valid; + logic unused_cfg_fields; + + 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_addr = cfg_addr_t'(reg_req_i.addr); + assign cfg_addr_in_window = reg_req_i.addr == RegAddrWidth'(cfg_addr); + assign cfg_status_access = reg_req_i.valid && + cfg_addr_in_window && (cfg_addr == 12'h010); + + always_comb begin : proc_cfg_addr_mapped + sel_reg_mapped = cfg_addr_in_window && + ((cfg_addr == 12'h000) || + (cfg_addr == 12'h004) || + (cfg_addr == 12'h008) || + (cfg_addr == 12'h00c) || + (cfg_addr == 12'h010) || + (cfg_addr == 12'h100) || + (cfg_addr == 12'h200) || + (cfg_addr == 12'hffc)); + for (int unsigned i = 0; i < 2; i++) begin + sel_reg_mapped |= cfg_addr_in_window && + ((cfg_addr == (12'h300 + i * 12'h40)) || + (cfg_addr == (12'h304 + i * 12'h40))); + end + for (int unsigned i = 0; i < 8; i++) begin + sel_reg_mapped |= cfg_addr_in_window && + ((cfg_addr == (12'h400 + i * 12'h40)) || + (cfg_addr == (12'h404 + i * 12'h40)) || + (cfg_addr == (12'h408 + i * 12'h40)) || + (cfg_addr == (12'h40c + i * 12'h40)) || + (cfg_addr == (12'h410 + i * 12'h40)) || + (cfg_addr == (12'h414 + i * 12'h40)) || + (cfg_addr == (12'h418 + i * 12'h40))); end end + // The configuration frontend owns draining and automatic apply sequencing. + // Keep this wrapper purely responsible for the stable register-map access. + assign cfg_access_open = 1'b1; - // 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]}}; + always_comb begin : proc_cfg_value_valid + cfg_value_valid = 1'b1; + if (reg_req_i.valid && reg_req_i.write && sel_reg_mapped) begin + // All byte-backed legacy values are range-checked only when their + // corresponding byte is written, preserving partial-write behavior. + if ((cfg_addr == 12'h100) && reg_req_i.wstrb[0]) begin + cfg_value_valid &= (reg_req_i.wdata[7:0] <= 8'd1); + end + if ((cfg_addr == 12'h200) && reg_req_i.wstrb[0]) begin + cfg_value_valid &= (reg_req_i.wdata[7:0] >= 8'd2); + end + if (((cfg_addr == 12'h304) || (cfg_addr == 12'h344)) && + reg_req_i.wstrb[0]) begin + cfg_value_valid &= (reg_req_i.wdata[7:0] <= 8'd15); + end + for (int unsigned i = 0; i < 8; i++) begin + if (cfg_addr == (12'h408 + i * 12'h40)) begin + if (reg_req_i.wstrb[0]) cfg_value_valid &= (reg_req_i.wdata[7:0] <= 8'd1); + if (reg_req_i.wstrb[1]) cfg_value_valid &= (reg_req_i.wdata[15:8] <= 8'd31); + if (reg_req_i.wstrb[2]) cfg_value_valid &= (reg_req_i.wdata[23:16] <= 8'd1); + end + if (cfg_addr == (12'h40c + i * 12'h40)) begin + if (reg_req_i.wstrb[0]) cfg_value_valid &= + (reg_req_i.wdata[7:0] >= 8'd3 && reg_req_i.wdata[7:0] <= 8'd15); + if (reg_req_i.wstrb[1]) cfg_value_valid &= (reg_req_i.wdata[15:8] <= 8'd15); + if (reg_req_i.wstrb[2]) cfg_value_valid &= (reg_req_i.wdata[23:16] <= 8'd1); + end + if (cfg_addr == (12'h414 + i * 12'h40)) begin + if (reg_req_i.wstrb[0]) cfg_value_valid &= (reg_req_i.wdata[7:0] <= 8'd15); + if (reg_req_i.wstrb[1]) cfg_value_valid &= (reg_req_i.wdata[15:8] <= 8'd15); + if (reg_req_i.wstrb[2]) cfg_value_valid &= (reg_req_i.wdata[23:16] <= 8'd15); + end + end + end end - // 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); - end - endcase // sel_reg + assign reg_rsp_o.ready = cfg_access_open & + (~reg_req_i.valid | ~sel_reg_mapped | ~cfg_value_valid | cfg_reg_rsp.ready); + assign reg_rsp_o.error = (reg_req_i.valid && ~sel_reg_mapped) | + (reg_req_i.valid && reg_req_i.write && sel_reg_mapped && !cfg_value_valid) | + 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 & cfg_value_valid; + assign cfg_reg_req.addr = cfg_addr; + 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 && !cfg_status_access) 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 - 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 + `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 < hyperbus_pkg::HyperNumChips; i++) begin + chip_rules_all[i].idx = unsigned'(i); + end + chip_rules_all[0].start_addr = {cfg_hwif_out.chip_0.range_base.value.value, 22'b0}; + chip_rules_all[0].end_addr = {cfg_hwif_out.chip_0.range_bound.value.value, 22'b0}; + chip_rules_all[1].start_addr = {cfg_hwif_out.chip_1.range_base.value.value, 22'b0}; + chip_rules_all[1].end_addr = {cfg_hwif_out.chip_1.range_bound.value.value, 22'b0}; + chip_rules_all[2].start_addr = {cfg_hwif_out.chip_2.range_base.value.value, 22'b0}; + chip_rules_all[2].end_addr = {cfg_hwif_out.chip_2.range_bound.value.value, 22'b0}; + chip_rules_all[3].start_addr = {cfg_hwif_out.chip_3.range_base.value.value, 22'b0}; + chip_rules_all[3].end_addr = {cfg_hwif_out.chip_3.range_bound.value.value, 22'b0}; + chip_rules_all[4].start_addr = {cfg_hwif_out.chip_4.range_base.value.value, 22'b0}; + chip_rules_all[4].end_addr = {cfg_hwif_out.chip_4.range_bound.value.value, 22'b0}; + chip_rules_all[5].start_addr = {cfg_hwif_out.chip_5.range_base.value.value, 22'b0}; + chip_rules_all[5].end_addr = {cfg_hwif_out.chip_5.range_bound.value.value, 22'b0}; + chip_rules_all[6].start_addr = {cfg_hwif_out.chip_6.range_base.value.value, 22'b0}; + chip_rules_all[6].end_addr = {cfg_hwif_out.chip_6.range_bound.value.value, 22'b0}; + chip_rules_all[7].start_addr = {cfg_hwif_out.chip_7.range_base.value.value, 22'b0}; + chip_rules_all[7].end_addr = {cfg_hwif_out.chip_7.range_bound.value.value, 22'b0}; end - // Registers - `FFARN(cfg_q, cfg_d, RstCfg, clk_i, rst_ni); - `FFARN(crange_q, crange_d, crange_rstval, clk_i, rst_ni); + assign cfg_hwif_in.global_cfg.capability.num_chips.next = 8'(hyperbus_pkg::HyperNumChips); + assign cfg_hwif_in.global_cfg.capability.num_phys.next = 8'(NumPhys); + assign cfg_hwif_in.global_cfg.capability.per_chip_cfg.next = CapabilityFeatures[0]; + assign cfg_hwif_in.global_cfg.capability.per_phy_cfg.next = CapabilityFeatures[1]; + assign cfg_hwif_in.global_cfg.capability.chip_enable.next = CapabilityFeatures[2]; + assign cfg_hwif_in.global_cfg.capability.clock_divider.next = CapabilityFeatures[3]; + assign cfg_hwif_in.global_cfg.capability.staged_apply.next = CapabilityFeatures[4]; + assign cfg_hwif_in.global_cfg.capability.error_status.next = CapabilityFeatures[5]; + assign cfg_hwif_in.global_cfg.capability.rwds_sample_timing.next = + CapabilityFeatures[6]; + assign cfg_hwif_in.global_cfg.capability.rwds_oe_timing.next = CapabilityFeatures[7]; + assign cfg_hwif_in.global_cfg.status.decode_error.next = + cfg_hwif_out.global_cfg.status.decode_error.value; + assign cfg_hwif_in.global_cfg.status.decode_error.hwset = decode_error_i; + assign cfg_hwif_in.global_cfg.status.busy.next = 1'b0; + assign cfg_hwif_in.global_cfg.status.dirty.next = 1'b0; + // COMMAND fields are intentionally left unconsumed until staged apply is + // implemented. STATUS busy/dirty likewise have no point-3 source. + + 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_in ( cfg_hwif_in ), + .hwif_out ( cfg_hwif_out ) + ); + + always_comb begin : proc_cfg_output + frontend_cfg_o = '0; + phy_cfg_o = '0; + + frontend_cfg_o.address_mask_msb = cfg_hwif_out.chip_0.address_cfg.address_mask_msb.value; + frontend_cfg_o.address_space = cfg_hwif_out.chip_0.address_cfg.address_space.value[0]; + frontend_cfg_o.dual_phy = (NumPhys == 2) && + cfg_hwif_out.frontend.frontend_cfg.dual_phy.value; + frontend_cfg_o.phy_clock_div = cfg_hwif_out.backend.clock_cfg.divider.value; + + phy_cfg_o.chip.t_latency_access = cfg_hwif_out.chip_0.latency_cfg.t_latency_access.value; + phy_cfg_o.chip.en_latency_additional = cfg_hwif_out.chip_0.latency_cfg.en_latency_additional.value; + phy_cfg_o.chip.t_burst_max = cfg_hwif_out.chip_0.burst_cfg.t_burst_max.value; + phy_cfg_o.chip.t_read_write_recovery = cfg_hwif_out.chip_0.chip_timing.t_read_write_recovery.value; + phy_cfg_o.chip.t_rx_clk_delay = cfg_hwif_out.chip_0.rx_delay.value.value; + phy_cfg_o.chip.t_csh_cycles = cfg_hwif_out.chip_0.chip_timing.t_csh_cycles.value; + phy_cfg_o.chip.csn_to_ck_cycles = cfg_hwif_out.chip_0.chip_timing.csn_to_ck_cycles.value; + phy_cfg_o.t_tx_clk_delay = cfg_hwif_out.phy_0.tx_delay.value.value; + phy_cfg_o.dual_phy = (NumPhys == 2) && + cfg_hwif_out.frontend.frontend_cfg.dual_phy.value; + end - // 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]; + // The legacy PR34 interface has no destinations for these newer fields. + always_comb begin : proc_unused_cfg_fields + unused_cfg_fields = ^cfg_hwif_out.backend.clock_cfg.divider.value; + unused_cfg_fields |= ^cfg_hwif_out.phy_0.rwds_timing.rwds_oe_setup_cycles.value; + unused_cfg_fields |= ^cfg_hwif_out.phy_1.rwds_timing.rwds_oe_setup_cycles.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_0.address_cfg.enable.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_0.latency_cfg.rwds_sample_delay.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_1.address_cfg.enable.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_1.latency_cfg.rwds_sample_delay.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_2.address_cfg.enable.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_2.latency_cfg.rwds_sample_delay.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_3.address_cfg.enable.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_3.latency_cfg.rwds_sample_delay.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_4.address_cfg.enable.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_4.latency_cfg.rwds_sample_delay.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_5.address_cfg.enable.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_5.latency_cfg.rwds_sample_delay.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_6.address_cfg.enable.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_6.latency_cfg.rwds_sample_delay.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_7.address_cfg.enable.value; + unused_cfg_fields |= ^cfg_hwif_out.chip_7.latency_cfg.rwds_sample_delay.value; 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."); - `endif - // pragma translate_on + assign chip_rules_o = chip_rules_all; endmodule : hyperbus_cfg_regs 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_iso_bridge.sv b/src/hyperbus_iso_bridge.sv new file mode 100644 index 0000000..c751863 --- /dev/null +++ b/src/hyperbus_iso_bridge.sv @@ -0,0 +1,158 @@ +// 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" + +module hyperbus_iso_bridge #( + parameter type hyper_rx_t = logic, + parameter type hyper_tx_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, + input logic clk_phy_i, + input logic rst_phy_ni, + + 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, + + 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 +); + + //////////////////////////// + // 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; + + always_comb begin : proc_cfg_apply_pending + cfg_apply_pending_d = cfg_apply_pending_q; + + 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_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) + + 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; + + ////////////////////// + // 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 ), + .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_req_o.cmd_valid ), + .dst_ready_i ( backend_rsp_i.cmd_ready ) + ); + + 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 ), + .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_rsp_o.wrsp = wrsp_data_q; + + /////////////////////// + // 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 ( 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 ), + .dst_ready_i ( backend_rsp_i.tx_ready ), + .dst_data_o ( backend_req_o.tx ) + ); + + /////////////////////// + // 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 ( 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 ( frontend_rsp_o.rx_valid ), + .dst_ready_i ( frontend_req_i.rx_ready ), + .dst_data_o ( frontend_rsp_o.rx ) + ); + +endmodule diff --git a/src/hyperbus_isochronous.sv b/src/hyperbus_isochronous.sv new file mode 100644 index 0000000..74b4d62 --- /dev/null +++ b/src/hyperbus_isochronous.sv @@ -0,0 +1,331 @@ +// 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 "hyperbus/typedef.svh" +`include "common_cells/assertions.svh" +`include "common_cells/registers.svh" + +module hyperbus_isochronous #( + 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 AxiMaxReadTxns = 4, + parameter int unsigned AxiMaxWriteTxns = 4, + parameter int unsigned HostWriteBufferBytes = 64, + 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][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, + 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 +); + + `ASSERT_INIT(AxiAddrWidthValid, AxiAddrWidth >= $clog2(AxiDataWidth / 8)) + `ASSERT_INIT(AxiMaxReadTxnsValid, AxiMaxReadTxns >= 1) + `ASSERT_INIT(AxiMaxWriteTxnsValid, AxiMaxWriteTxns >= 1) + + 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; + localparam int unsigned AxiDataBytes = AxiDataWidth / 8; + `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) + `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys) + + ///////////////////// + // Clock and reset // + ///////////////////// + + logic clk_backend; + logic rst_backend_n; + + //////////////////////// + // Configuration path // + //////////////////////// + + 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; + + hyperbus_pkg::frontend_cfg_t frontend_cfg; + axi_rule_t [hyperbus_pkg::HyperNumChips-1:0] frontend_chip_rules; + + //////////////////// + // Dataflow links // + //////////////////// + + host_req_t host_req; + host_rsp_t host_rsp; + hyper_req_t midend_req; + hyper_rsp_t midend_rsp; + logic midend_trans_active; + logic midend_decode_error; + 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; + 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_sys_ni ), + .test_mode_i ( test_mode_i ), + .rst_no ( rst_backend_n ), + .init_no ( ) + ); + + //////////////////////////// + // Configuration frontend // + //////////////////////////// + + hyperbus_cfg_frontend #( + .NumPhys ( NumPhys ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .host_rule_t ( axi_rule_t ), + .RegDataWidth ( RegDataWidth ), + // Isochronous top implements the backend clock divider; decode-error + // status is shared by all top-level clocking variants. + .CapabilityFeatures ( 8'b0010_1000 ) + ) 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 ), + .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 ) + ); + + ////////////////// + // AXI frontend // + ////////////////// + + hyperbus_axi_frontend #( + .AxiDataWidth ( AxiDataWidth ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .AxiMaxReadTxns ( AxiMaxReadTxns ), + .AxiMaxWriteTxns ( AxiMaxWriteTxns ), + .MaxWriteDataBeats ( HostWriteBufferBytes / AxiDataBytes ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .host_req_t ( host_req_t ), + .host_rsp_t ( host_rsp_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_rsp_i ( host_rsp ) + ); + + //////////// + // Midend // + //////////// + + hyperbus_midend #( + .HostAddrWidth ( AxiAddrWidth ), + .HostDataWidth ( AxiDataWidth ), + .NumPhys ( NumPhys ), + .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 ), + .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 ), + .hyper_link_req_o ( midend_req ), + .hyper_link_rsp_i ( midend_rsp ) + ); + + //////////////////////// + // Isochronous bridge // + //////////////////////// + + hyperbus_iso_bridge #( + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_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 ), + .clk_phy_i ( clk_backend ), + .rst_phy_ni ( rst_backend_n ), + .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_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 ) + ); + + ///////////// + // Backend // + ///////////// + + hyperbus_backend #( + .NumPhys ( NumPhys ), + .StartupCycles ( PhyStartupCycles ), + .SyncStages ( SyncStages ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ) + ) i_backend ( + .clk_i ( clk_backend ), + .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 ( ), + .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 ), + .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_midend.sv b/src/hyperbus_midend.sv new file mode 100644 index 0000000..8b25cf2 --- /dev/null +++ b/src/hyperbus_midend.sv @@ -0,0 +1,718 @@ +// 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 NumPhys = -1, + parameter int unsigned HostWriteBufferBytes = 64, + 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_link_req_i, + output host_rsp_t host_link_rsp_o, + + input hyperbus_pkg::frontend_cfg_t frontend_cfg_i, + input rule_t [hyperbus_pkg::HyperNumChips-1:0] chip_rules_i, + output logic trans_active_o, + output logic decode_error_o, + + 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 WriteFifoDepth = HostWriteBufferBytes / HostDataBytes; + localparam int unsigned CommandFifoDepth = 2; + localparam int unsigned ReadFifoDepth = 4; + localparam int unsigned WriteRspFifoDepth = 4; + localparam int unsigned ChipSelWidth = + cf_math_pkg::idx_width(hyperbus_pkg::HyperNumChips); + + `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(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; + + 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; + 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; + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( CommandFifoDepth ), + .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; + 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 = 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; + 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_segment_complete; + 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 = + (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 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 && 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 = 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 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; + 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_cmd_i; + if (segment_pending_q) begin + cmd_req = segment_req_q; + end + if (atomic_active) begin + cmd_req = atomic_cmd; + 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_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_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 < 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; + end + end + end + + assign atomic_range_valid = req_range_valid && + (cmd_chip_sel_idx == cmd_end_chip_sel_idx); + + assign req_decode_error = normal_req && + (!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; + 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; + 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 + + 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 #( + .HostDataWidth ( HostDataWidth ), + .BurstLength ( hyperbus_pkg::HyperBurstWidth ), + .T ( host_r_t ), + .NumPhys ( NumPhys ) + ) i_read_adapter ( + .clk_i, + .rst_ni, + .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 #( + .HostDataWidth ( HostDataWidth ), + .T ( host_w_t ), + .NumPhys ( NumPhys ) + ) i_write_adapter ( + .clk_i, + .rst_ni, + .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 #( + .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 ( 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 ), + .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_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 && + (normal_read_segment_complete || normal_write_segment_complete); + + 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_accepted) 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) + // 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; + if (error_beats_q == 1) begin + error_state_d = ErrorIdle; + 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; + 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 = 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 + 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_accepted && !segment_pending_q) || + error_req_accepted || atomic_req_accepted; + 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; + + 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_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, backend_command_accepted |-> + (command_stage_out.cmd.trans.burst != '0)) + +endmodule diff --git a/src/hyperbus_phy2r.sv b/src/hyperbus_phy2r.sv deleted file mode 100644 index 5415e4c..0000000 --- a/src/hyperbus_phy2r.sv +++ /dev/null @@ -1,155 +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 - -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) -) ( - 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 -); - - // 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); - - 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; - - logic [3:0] size_d, size_q; - T data_buffer_d, data_buffer_q; - - logic is_16_bw, is_8_bw; - logic [WordCntWidth-1:0] word_cnt; - logic enough_data; - logic sent_available_data; - logic [BurstLength-1:0] next_axi_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 sent_available_data = byte_axi_addr_d >= byte_phy_cnt_q; - 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); - - 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<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_read_adapter.sv b/src/hyperbus_read_adapter.sv new file mode 100644 index 0000000..3e7aac5 --- /dev/null +++ b/src/hyperbus_read_adapter.sv @@ -0,0 +1,253 @@ +// 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 + +`include "common_cells/registers.svh" + +module hyperbus_read_adapter #( + 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_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, + 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 +); + + 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); + + typedef enum logic [1:0] { + Idle, + 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; + logic converted_valid; + logic converted_ready; + + 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 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; + assign data_o.last = host_last; + assign data_o.atomic_ok = 1'b0; + + always_comb begin : proc_counters + byte_host_addr_d = byte_host_addr_q; + byte_phy_cnt_d = byte_phy_cnt_q; + last_addr_d = last_addr_q; + size_d = size_q; + + 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; + byte_phy_cnt_d[AddrWidth-1:0] = (start_addr_i >> NumPhys) << NumPhys; + last_addr_d = ((start_addr_i >> size_i) << size_i) + (burst_len_i << size_i); + size_d = size_i; + end + if (host_accepted) begin + byte_host_addr_d = ((byte_host_addr_q >> size_q) << size_q) + (1 << size_q); + end + 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_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_accepted) begin + data_buffer_d.resp = hyperbus_pkg::HyperRespOkay; + end + 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; + end + data_buffer_d.last = converted_last; + end + 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 = WaitPhy; + end + end + // Accept the first physical word of the host beat. + WaitPhy: begin + converted_ready = 1'b1; + if (converted_valid) begin + state_d = CollectPhy; + end + end + // Accumulate physical words until one complete host beat is available. + CollectPhy: begin + converted_ready = 1'b1; + if (enough_data) begin + state_d = EmitHost; + end + end + // Hold the assembled host beat until accepted. + EmitHost: begin + host_valid_o = enough_data_q; + converted_ready = !enough_data_q; + 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 ? CollectPhy : WaitPhy; + end + end + end + default: begin + state_d = Idle; + end + 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; + 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 + + ///////////////////// + // 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) + `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 : hyperbus_read_adapter 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..fdca6cd --- /dev/null +++ b/src/hyperbus_synchronous.sv @@ -0,0 +1,240 @@ +// 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" +`include "common_cells/assertions.svh" + +module hyperbus_synchronous #( + 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 AxiMaxReadTxns = 4, + parameter int unsigned AxiMaxWriteTxns = 4, + parameter int unsigned HostWriteBufferBytes = 64, + 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][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, + 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 +); + + `ASSERT_INIT(AxiAddrWidthValid, AxiAddrWidth >= $clog2(AxiDataWidth / 8)) + `ASSERT_INIT(AxiMaxReadTxnsValid, AxiMaxReadTxns >= 1) + `ASSERT_INIT(AxiMaxWriteTxnsValid, AxiMaxWriteTxns >= 1) + + 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; + localparam int unsigned AxiDataBytes = AxiDataWidth / 8; + `HYPERBUS_TYPEDEF_HOST_ALL_CT(host, host_addr_t, host_data_t, host_strb_t) + `HYPERBUS_TYPEDEF_LINK_ALL_CT(hyper, NumPhys) + + ///////////////////// + // Clock and reset // + ///////////////////// + + logic clk_backend; + logic rst_backend_n; + + //////////////////////// + // Configuration path // + //////////////////////// + + 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; + logic backend_cfg_apply_valid; + logic backend_cfg_apply_ready; + + hyperbus_pkg::frontend_cfg_t frontend_cfg; + axi_rule_t [hyperbus_pkg::HyperNumChips-1:0] frontend_chip_rules; + + //////////////////// + // Dataflow links // + //////////////////// + + host_req_t host_req; + host_rsp_t host_rsp; + hyper_req_t midend_req; + hyper_rsp_t midend_rsp; + logic midend_trans_active; + logic midend_decode_error; + hyper_req_t backend_req; + hyper_rsp_t backend_rsp; + + 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 backend_req = midend_req; + assign midend_rsp = backend_rsp; + + //////////////////////////// + // Configuration frontend // + //////////////////////////// + + hyperbus_cfg_frontend #( + .NumPhys ( NumPhys ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ), + .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 ), + .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 ) + ); + + ////////////////// + // AXI frontend // + ////////////////// + + hyperbus_axi_frontend #( + .AxiDataWidth ( AxiDataWidth ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .AxiMaxReadTxns ( AxiMaxReadTxns ), + .AxiMaxWriteTxns ( AxiMaxWriteTxns ), + .MaxWriteDataBeats ( HostWriteBufferBytes / AxiDataBytes ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ), + .host_req_t ( host_req_t ), + .host_rsp_t ( host_rsp_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_rsp_i ( host_rsp ) + ); + + //////////// + // Midend // + //////////// + + hyperbus_midend #( + .HostAddrWidth ( AxiAddrWidth ), + .HostDataWidth ( AxiDataWidth ), + .NumPhys ( NumPhys ), + .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 ), + .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 ), + .hyper_link_req_o ( midend_req ), + .hyper_link_rsp_i ( midend_rsp ) + ); + + ///////////// + // Backend // + ///////////// + + hyperbus_backend #( + .NumPhys ( NumPhys ), + .StartupCycles ( PhyStartupCycles ), + .SyncStages ( SyncStages ), + .hyper_rx_t ( hyper_rx_t ), + .hyper_tx_t ( hyper_tx_t ), + .hyper_req_t ( hyper_req_t ), + .hyper_rsp_t ( hyper_rsp_t ) + ) i_backend ( + .clk_i ( clk_backend ), + .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 ( ), + .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 ), + .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 8b1935e..0000000 --- a/src/hyperbus_synth_wrap.sv +++ /dev/null @@ -1,232 +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 ), - .RegAddrWidth ( RegAddrWidth ), - .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_w2phy.sv b/src/hyperbus_w2phy.sv deleted file mode 100644 index 78a04fa..0000000 --- a/src/hyperbus_w2phy.sv +++ /dev/null @@ -1,214 +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_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) -) ( - 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 -); - - 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<=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 +// Paul Scheffler +// Luca Valente + +`include "common_cells/registers.svh" + +module hyperbus_write_adapter #( + 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_i, + input logic [AddrWidth-1:0] start_addr_i, + input logic start_i, + input logic dual_phy_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 NumHostBytes = HostDataWidth / 8; + localparam int unsigned NumPhyBytes = NumPhys * 2; + localparam int unsigned PhyBeatsPerHost = NumHostBytes / NumPhyBytes; + localparam int unsigned WordCntWidth = + (PhyBeatsPerHost == 1) ? 1 : $clog2(PhyBeatsPerHost); + + typedef enum logic [1:0] { + Idle, + CollectHost, + EmitPhy + } write_adapter_state_e; + + typedef struct packed { + logic [HostDataWidth/8-1:0] strb; + logic [HostDataWidth-1:0] data; + 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 first_tx_d, first_tx_q; + logic [NumPhys*2-1:0] mask_strobe_d, mask_strobe_q; + 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 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; + size_d = size_q; + cnt_data_phy_d = cnt_data_phy_q; + first_tx_d = first_tx_q; + + if (start_i) begin + byte_idx_d = start_addr_i; + size_d = size_i; + cnt_data_phy_d = (start_addr_i >> NumPhys) << NumPhys; + first_tx_d = 1'b1; + end + 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_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_q == Idle) begin + data_buffer_d.last = 1'b0; + 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; + data_buffer_d.strb = data_i.strb; + data_buffer_d.last = data_i.last; + if (first_tx_q) begin + for (int unsigned i = 0; i < byte_idx_q; i++) begin + data_buffer_d.strb[i] = 1'b0; + end + end + end else begin + data_buffer_d.strb[byte_idx_q +: (2*NumPhys)] = + data_i.strb[byte_idx_q +: (2*NumPhys)]; + data_buffer_d.data[byte_idx_q*8 +: (8*NumPhys)] = + data_i.data[byte_idx_q*8 +: (8*NumPhys)]; + data_buffer_d.last = data_i.last; + if (first_tx_q) begin + for (int unsigned i = 0; i < byte_idx_q; i++) begin + data_buffer_d.strb[i] = 1'b0; + end + end + end + 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; + host_ready_o = 1'b0; + converted_valid = 1'b0; + + unique case (state_q) + // Wait for a command to initialize address tracking. + Idle: begin + if (start_i) begin + state_d = CollectHost; + end + end + // Gather enough host bytes to form the next physical word. + CollectHost: begin + host_ready_o = 1'b1; + if (collect_complete) begin + state_d = EmitPhy; + end + end + // Emit physical words and return for more host data when needed. + EmitPhy: begin + converted_valid = 1'b1; + 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 + state_d = Idle; + end + endcase + end + + ////////////////////////// + // Physical-width adapter // + ////////////////////////// + + 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 + + ///////////////////// + // 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) + `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 : hyperbus_write_adapter diff --git a/src/regs/hyperbus_cfg_regblock.sv b/src/regs/hyperbus_cfg_regblock.sv new file mode 100644 index 0000000..ec72f2c --- /dev/null +++ b/src/regs/hyperbus_cfg_regblock.sv @@ -0,0 +1,4665 @@ +// 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 [11: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, + + input hyperbus_cfg_regblock_pkg::hyperbus_cfg_regs__in_t hwif_in, + 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 [11: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[11: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 { + struct { + logic ip_version; + logic reg_if_version; + logic capability; + logic command; + logic status; + } global_cfg; + struct { + logic frontend_cfg; + } frontend; + struct { + logic clock_cfg; + } backend; + struct { + logic tx_delay; + logic rwds_timing; + } phy_0; + struct { + logic tx_delay; + logic rwds_timing; + } phy_1; + struct { + logic range_base; + logic range_bound; + logic address_cfg; + logic latency_cfg; + logic burst_cfg; + logic chip_timing; + logic rx_delay; + } chip_0; + struct { + logic range_base; + logic range_bound; + logic address_cfg; + logic latency_cfg; + logic burst_cfg; + logic chip_timing; + logic rx_delay; + } chip_1; + struct { + logic range_base; + logic range_bound; + logic address_cfg; + logic latency_cfg; + logic burst_cfg; + logic chip_timing; + logic rx_delay; + } chip_2; + struct { + logic range_base; + logic range_bound; + logic address_cfg; + logic latency_cfg; + logic burst_cfg; + logic chip_timing; + logic rx_delay; + } chip_3; + struct { + logic range_base; + logic range_bound; + logic address_cfg; + logic latency_cfg; + logic burst_cfg; + logic chip_timing; + logic rx_delay; + } chip_4; + struct { + logic range_base; + logic range_bound; + logic address_cfg; + logic latency_cfg; + logic burst_cfg; + logic chip_timing; + logic rx_delay; + } chip_5; + struct { + logic range_base; + logic range_bound; + logic address_cfg; + logic latency_cfg; + logic burst_cfg; + logic chip_timing; + logic rx_delay; + } chip_6; + struct { + logic range_base; + logic range_bound; + logic address_cfg; + logic latency_cfg; + logic burst_cfg; + logic chip_timing; + logic rx_delay; + } chip_7; + logic reserved_top; + } decoded_reg_strb_t; + decoded_reg_strb_t decoded_reg_strb; + logic decoded_err; + logic [11: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.global_cfg.ip_version = cpuif_req_masked & (cpuif_addr == 12'h0) & !cpuif_req_is_wr; + decoded_reg_strb.global_cfg.reg_if_version = cpuif_req_masked & (cpuif_addr == 12'h4) & !cpuif_req_is_wr; + decoded_reg_strb.global_cfg.capability = cpuif_req_masked & (cpuif_addr == 12'h8) & !cpuif_req_is_wr; + decoded_reg_strb.global_cfg.command = cpuif_req_masked & (cpuif_addr == 12'hc); + decoded_reg_strb.global_cfg.status = cpuif_req_masked & (cpuif_addr == 12'h10); + decoded_reg_strb.frontend.frontend_cfg = cpuif_req_masked & (cpuif_addr == 12'h100); + decoded_reg_strb.backend.clock_cfg = cpuif_req_masked & (cpuif_addr == 12'h200); + decoded_reg_strb.phy_0.tx_delay = cpuif_req_masked & (cpuif_addr == 12'h300); + decoded_reg_strb.phy_0.rwds_timing = cpuif_req_masked & (cpuif_addr == 12'h304); + decoded_reg_strb.phy_1.tx_delay = cpuif_req_masked & (cpuif_addr == 12'h340); + decoded_reg_strb.phy_1.rwds_timing = cpuif_req_masked & (cpuif_addr == 12'h344); + decoded_reg_strb.chip_0.range_base = cpuif_req_masked & (cpuif_addr == 12'h400); + decoded_reg_strb.chip_0.range_bound = cpuif_req_masked & (cpuif_addr == 12'h404); + decoded_reg_strb.chip_0.address_cfg = cpuif_req_masked & (cpuif_addr == 12'h408); + decoded_reg_strb.chip_0.latency_cfg = cpuif_req_masked & (cpuif_addr == 12'h40c); + decoded_reg_strb.chip_0.burst_cfg = cpuif_req_masked & (cpuif_addr == 12'h410); + decoded_reg_strb.chip_0.chip_timing = cpuif_req_masked & (cpuif_addr == 12'h414); + decoded_reg_strb.chip_0.rx_delay = cpuif_req_masked & (cpuif_addr == 12'h418); + decoded_reg_strb.chip_1.range_base = cpuif_req_masked & (cpuif_addr == 12'h440); + decoded_reg_strb.chip_1.range_bound = cpuif_req_masked & (cpuif_addr == 12'h444); + decoded_reg_strb.chip_1.address_cfg = cpuif_req_masked & (cpuif_addr == 12'h448); + decoded_reg_strb.chip_1.latency_cfg = cpuif_req_masked & (cpuif_addr == 12'h44c); + decoded_reg_strb.chip_1.burst_cfg = cpuif_req_masked & (cpuif_addr == 12'h450); + decoded_reg_strb.chip_1.chip_timing = cpuif_req_masked & (cpuif_addr == 12'h454); + decoded_reg_strb.chip_1.rx_delay = cpuif_req_masked & (cpuif_addr == 12'h458); + decoded_reg_strb.chip_2.range_base = cpuif_req_masked & (cpuif_addr == 12'h480); + decoded_reg_strb.chip_2.range_bound = cpuif_req_masked & (cpuif_addr == 12'h484); + decoded_reg_strb.chip_2.address_cfg = cpuif_req_masked & (cpuif_addr == 12'h488); + decoded_reg_strb.chip_2.latency_cfg = cpuif_req_masked & (cpuif_addr == 12'h48c); + decoded_reg_strb.chip_2.burst_cfg = cpuif_req_masked & (cpuif_addr == 12'h490); + decoded_reg_strb.chip_2.chip_timing = cpuif_req_masked & (cpuif_addr == 12'h494); + decoded_reg_strb.chip_2.rx_delay = cpuif_req_masked & (cpuif_addr == 12'h498); + decoded_reg_strb.chip_3.range_base = cpuif_req_masked & (cpuif_addr == 12'h4c0); + decoded_reg_strb.chip_3.range_bound = cpuif_req_masked & (cpuif_addr == 12'h4c4); + decoded_reg_strb.chip_3.address_cfg = cpuif_req_masked & (cpuif_addr == 12'h4c8); + decoded_reg_strb.chip_3.latency_cfg = cpuif_req_masked & (cpuif_addr == 12'h4cc); + decoded_reg_strb.chip_3.burst_cfg = cpuif_req_masked & (cpuif_addr == 12'h4d0); + decoded_reg_strb.chip_3.chip_timing = cpuif_req_masked & (cpuif_addr == 12'h4d4); + decoded_reg_strb.chip_3.rx_delay = cpuif_req_masked & (cpuif_addr == 12'h4d8); + decoded_reg_strb.chip_4.range_base = cpuif_req_masked & (cpuif_addr == 12'h500); + decoded_reg_strb.chip_4.range_bound = cpuif_req_masked & (cpuif_addr == 12'h504); + decoded_reg_strb.chip_4.address_cfg = cpuif_req_masked & (cpuif_addr == 12'h508); + decoded_reg_strb.chip_4.latency_cfg = cpuif_req_masked & (cpuif_addr == 12'h50c); + decoded_reg_strb.chip_4.burst_cfg = cpuif_req_masked & (cpuif_addr == 12'h510); + decoded_reg_strb.chip_4.chip_timing = cpuif_req_masked & (cpuif_addr == 12'h514); + decoded_reg_strb.chip_4.rx_delay = cpuif_req_masked & (cpuif_addr == 12'h518); + decoded_reg_strb.chip_5.range_base = cpuif_req_masked & (cpuif_addr == 12'h540); + decoded_reg_strb.chip_5.range_bound = cpuif_req_masked & (cpuif_addr == 12'h544); + decoded_reg_strb.chip_5.address_cfg = cpuif_req_masked & (cpuif_addr == 12'h548); + decoded_reg_strb.chip_5.latency_cfg = cpuif_req_masked & (cpuif_addr == 12'h54c); + decoded_reg_strb.chip_5.burst_cfg = cpuif_req_masked & (cpuif_addr == 12'h550); + decoded_reg_strb.chip_5.chip_timing = cpuif_req_masked & (cpuif_addr == 12'h554); + decoded_reg_strb.chip_5.rx_delay = cpuif_req_masked & (cpuif_addr == 12'h558); + decoded_reg_strb.chip_6.range_base = cpuif_req_masked & (cpuif_addr == 12'h580); + decoded_reg_strb.chip_6.range_bound = cpuif_req_masked & (cpuif_addr == 12'h584); + decoded_reg_strb.chip_6.address_cfg = cpuif_req_masked & (cpuif_addr == 12'h588); + decoded_reg_strb.chip_6.latency_cfg = cpuif_req_masked & (cpuif_addr == 12'h58c); + decoded_reg_strb.chip_6.burst_cfg = cpuif_req_masked & (cpuif_addr == 12'h590); + decoded_reg_strb.chip_6.chip_timing = cpuif_req_masked & (cpuif_addr == 12'h594); + decoded_reg_strb.chip_6.rx_delay = cpuif_req_masked & (cpuif_addr == 12'h598); + decoded_reg_strb.chip_7.range_base = cpuif_req_masked & (cpuif_addr == 12'h5c0); + decoded_reg_strb.chip_7.range_bound = cpuif_req_masked & (cpuif_addr == 12'h5c4); + decoded_reg_strb.chip_7.address_cfg = cpuif_req_masked & (cpuif_addr == 12'h5c8); + decoded_reg_strb.chip_7.latency_cfg = cpuif_req_masked & (cpuif_addr == 12'h5cc); + decoded_reg_strb.chip_7.burst_cfg = cpuif_req_masked & (cpuif_addr == 12'h5d0); + decoded_reg_strb.chip_7.chip_timing = cpuif_req_masked & (cpuif_addr == 12'h5d4); + decoded_reg_strb.chip_7.rx_delay = cpuif_req_masked & (cpuif_addr == 12'h5d8); + decoded_reg_strb.reserved_top = cpuif_req_masked & (cpuif_addr == 12'hffc) & !cpuif_req_is_wr; + 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 { + struct { + logic [7:0] next; + logic load_next; + } num_chips; + struct { + logic [7:0] next; + logic load_next; + } num_phys; + struct { + logic next; + logic load_next; + } per_chip_cfg; + struct { + logic next; + logic load_next; + } per_phy_cfg; + struct { + logic next; + logic load_next; + } chip_enable; + struct { + logic next; + logic load_next; + } clock_divider; + struct { + logic next; + logic load_next; + } staged_apply; + struct { + logic next; + logic load_next; + } error_status; + struct { + logic next; + logic load_next; + } rwds_sample_timing; + struct { + logic next; + logic load_next; + } rwds_oe_timing; + } capability; + struct { + struct { + logic next; + logic load_next; + } flush; + struct { + logic next; + logic load_next; + } apply; + } command; + struct { + struct { + logic next; + logic load_next; + } decode_error; + struct { + logic next; + logic load_next; + } busy; + struct { + logic next; + logic load_next; + } dirty; + } status; + } global_cfg; + struct { + struct { + struct { + logic next; + logic load_next; + } dual_phy; + } frontend_cfg; + } frontend; + struct { + struct { + struct { + logic [7:0] next; + logic load_next; + } divider; + } clock_cfg; + } backend; + struct { + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } tx_delay; + struct { + struct { + logic [7:0] next; + logic load_next; + } rwds_oe_setup_cycles; + } rwds_timing; + } phy_0; + struct { + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } tx_delay; + struct { + struct { + logic [7:0] next; + logic load_next; + } rwds_oe_setup_cycles; + } rwds_timing; + } phy_1; + struct { + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_bound; + struct { + struct { + logic [7:0] next; + logic load_next; + } address_space; + struct { + logic [7:0] next; + logic load_next; + } address_mask_msb; + struct { + logic next; + logic load_next; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_latency_access; + struct { + logic [7:0] next; + logic load_next; + } rwds_sample_delay; + struct { + logic next; + logic load_next; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] next; + logic load_next; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_read_write_recovery; + struct { + logic [7:0] next; + logic load_next; + } t_csh_cycles; + struct { + logic [7:0] next; + logic load_next; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } rx_delay; + } chip_0; + struct { + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_bound; + struct { + struct { + logic [7:0] next; + logic load_next; + } address_space; + struct { + logic [7:0] next; + logic load_next; + } address_mask_msb; + struct { + logic next; + logic load_next; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_latency_access; + struct { + logic [7:0] next; + logic load_next; + } rwds_sample_delay; + struct { + logic next; + logic load_next; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] next; + logic load_next; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_read_write_recovery; + struct { + logic [7:0] next; + logic load_next; + } t_csh_cycles; + struct { + logic [7:0] next; + logic load_next; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } rx_delay; + } chip_1; + struct { + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_bound; + struct { + struct { + logic [7:0] next; + logic load_next; + } address_space; + struct { + logic [7:0] next; + logic load_next; + } address_mask_msb; + struct { + logic next; + logic load_next; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_latency_access; + struct { + logic [7:0] next; + logic load_next; + } rwds_sample_delay; + struct { + logic next; + logic load_next; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] next; + logic load_next; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_read_write_recovery; + struct { + logic [7:0] next; + logic load_next; + } t_csh_cycles; + struct { + logic [7:0] next; + logic load_next; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } rx_delay; + } chip_2; + struct { + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_bound; + struct { + struct { + logic [7:0] next; + logic load_next; + } address_space; + struct { + logic [7:0] next; + logic load_next; + } address_mask_msb; + struct { + logic next; + logic load_next; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_latency_access; + struct { + logic [7:0] next; + logic load_next; + } rwds_sample_delay; + struct { + logic next; + logic load_next; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] next; + logic load_next; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_read_write_recovery; + struct { + logic [7:0] next; + logic load_next; + } t_csh_cycles; + struct { + logic [7:0] next; + logic load_next; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } rx_delay; + } chip_3; + struct { + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_bound; + struct { + struct { + logic [7:0] next; + logic load_next; + } address_space; + struct { + logic [7:0] next; + logic load_next; + } address_mask_msb; + struct { + logic next; + logic load_next; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_latency_access; + struct { + logic [7:0] next; + logic load_next; + } rwds_sample_delay; + struct { + logic next; + logic load_next; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] next; + logic load_next; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_read_write_recovery; + struct { + logic [7:0] next; + logic load_next; + } t_csh_cycles; + struct { + logic [7:0] next; + logic load_next; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } rx_delay; + } chip_4; + struct { + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_bound; + struct { + struct { + logic [7:0] next; + logic load_next; + } address_space; + struct { + logic [7:0] next; + logic load_next; + } address_mask_msb; + struct { + logic next; + logic load_next; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_latency_access; + struct { + logic [7:0] next; + logic load_next; + } rwds_sample_delay; + struct { + logic next; + logic load_next; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] next; + logic load_next; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_read_write_recovery; + struct { + logic [7:0] next; + logic load_next; + } t_csh_cycles; + struct { + logic [7:0] next; + logic load_next; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } rx_delay; + } chip_5; + struct { + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_bound; + struct { + struct { + logic [7:0] next; + logic load_next; + } address_space; + struct { + logic [7:0] next; + logic load_next; + } address_mask_msb; + struct { + logic next; + logic load_next; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_latency_access; + struct { + logic [7:0] next; + logic load_next; + } rwds_sample_delay; + struct { + logic next; + logic load_next; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] next; + logic load_next; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_read_write_recovery; + struct { + logic [7:0] next; + logic load_next; + } t_csh_cycles; + struct { + logic [7:0] next; + logic load_next; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } rx_delay; + } chip_6; + struct { + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_base; + struct { + struct { + logic [9:0] next; + logic load_next; + } value; + } range_bound; + struct { + struct { + logic [7:0] next; + logic load_next; + } address_space; + struct { + logic [7:0] next; + logic load_next; + } address_mask_msb; + struct { + logic next; + logic load_next; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_latency_access; + struct { + logic [7:0] next; + logic load_next; + } rwds_sample_delay; + struct { + logic next; + logic load_next; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] next; + logic load_next; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] next; + logic load_next; + } t_read_write_recovery; + struct { + logic [7:0] next; + logic load_next; + } t_csh_cycles; + struct { + logic [7:0] next; + logic load_next; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] next; + logic load_next; + } value; + } rx_delay; + } chip_7; + } field_combo_t; + field_combo_t field_combo; + + typedef struct { + struct { + struct { + struct { + logic [7:0] value; + } num_chips; + struct { + logic [7:0] value; + } num_phys; + struct { + logic value; + } per_chip_cfg; + struct { + logic value; + } per_phy_cfg; + struct { + logic value; + } chip_enable; + struct { + logic value; + } clock_divider; + struct { + logic value; + } staged_apply; + struct { + logic value; + } error_status; + struct { + logic value; + } rwds_sample_timing; + struct { + logic value; + } rwds_oe_timing; + } capability; + struct { + struct { + logic value; + } flush; + struct { + logic value; + } apply; + } command; + struct { + struct { + logic value; + } decode_error; + struct { + logic value; + } busy; + struct { + logic value; + } dirty; + } status; + } global_cfg; + struct { + struct { + struct { + logic value; + } dual_phy; + } frontend_cfg; + } frontend; + struct { + struct { + struct { + logic [7:0] value; + } divider; + } clock_cfg; + } backend; + struct { + struct { + struct { + logic [7:0] value; + } value; + } tx_delay; + struct { + struct { + logic [7:0] value; + } rwds_oe_setup_cycles; + } rwds_timing; + } phy_0; + struct { + struct { + struct { + logic [7:0] value; + } value; + } tx_delay; + struct { + struct { + logic [7:0] value; + } rwds_oe_setup_cycles; + } rwds_timing; + } phy_1; + struct { + struct { + struct { + logic [9:0] value; + } value; + } range_base; + struct { + struct { + logic [9:0] value; + } value; + } range_bound; + struct { + struct { + logic [7:0] value; + } address_space; + struct { + logic [7:0] value; + } address_mask_msb; + struct { + logic value; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] value; + } t_latency_access; + struct { + logic [7:0] value; + } rwds_sample_delay; + struct { + logic value; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] value; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] value; + } t_read_write_recovery; + struct { + logic [7:0] value; + } t_csh_cycles; + struct { + logic [7:0] value; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] value; + } value; + } rx_delay; + } chip_0; + struct { + struct { + struct { + logic [9:0] value; + } value; + } range_base; + struct { + struct { + logic [9:0] value; + } value; + } range_bound; + struct { + struct { + logic [7:0] value; + } address_space; + struct { + logic [7:0] value; + } address_mask_msb; + struct { + logic value; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] value; + } t_latency_access; + struct { + logic [7:0] value; + } rwds_sample_delay; + struct { + logic value; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] value; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] value; + } t_read_write_recovery; + struct { + logic [7:0] value; + } t_csh_cycles; + struct { + logic [7:0] value; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] value; + } value; + } rx_delay; + } chip_1; + struct { + struct { + struct { + logic [9:0] value; + } value; + } range_base; + struct { + struct { + logic [9:0] value; + } value; + } range_bound; + struct { + struct { + logic [7:0] value; + } address_space; + struct { + logic [7:0] value; + } address_mask_msb; + struct { + logic value; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] value; + } t_latency_access; + struct { + logic [7:0] value; + } rwds_sample_delay; + struct { + logic value; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] value; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] value; + } t_read_write_recovery; + struct { + logic [7:0] value; + } t_csh_cycles; + struct { + logic [7:0] value; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] value; + } value; + } rx_delay; + } chip_2; + struct { + struct { + struct { + logic [9:0] value; + } value; + } range_base; + struct { + struct { + logic [9:0] value; + } value; + } range_bound; + struct { + struct { + logic [7:0] value; + } address_space; + struct { + logic [7:0] value; + } address_mask_msb; + struct { + logic value; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] value; + } t_latency_access; + struct { + logic [7:0] value; + } rwds_sample_delay; + struct { + logic value; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] value; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] value; + } t_read_write_recovery; + struct { + logic [7:0] value; + } t_csh_cycles; + struct { + logic [7:0] value; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] value; + } value; + } rx_delay; + } chip_3; + struct { + struct { + struct { + logic [9:0] value; + } value; + } range_base; + struct { + struct { + logic [9:0] value; + } value; + } range_bound; + struct { + struct { + logic [7:0] value; + } address_space; + struct { + logic [7:0] value; + } address_mask_msb; + struct { + logic value; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] value; + } t_latency_access; + struct { + logic [7:0] value; + } rwds_sample_delay; + struct { + logic value; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] value; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] value; + } t_read_write_recovery; + struct { + logic [7:0] value; + } t_csh_cycles; + struct { + logic [7:0] value; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] value; + } value; + } rx_delay; + } chip_4; + struct { + struct { + struct { + logic [9:0] value; + } value; + } range_base; + struct { + struct { + logic [9:0] value; + } value; + } range_bound; + struct { + struct { + logic [7:0] value; + } address_space; + struct { + logic [7:0] value; + } address_mask_msb; + struct { + logic value; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] value; + } t_latency_access; + struct { + logic [7:0] value; + } rwds_sample_delay; + struct { + logic value; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] value; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] value; + } t_read_write_recovery; + struct { + logic [7:0] value; + } t_csh_cycles; + struct { + logic [7:0] value; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] value; + } value; + } rx_delay; + } chip_5; + struct { + struct { + struct { + logic [9:0] value; + } value; + } range_base; + struct { + struct { + logic [9:0] value; + } value; + } range_bound; + struct { + struct { + logic [7:0] value; + } address_space; + struct { + logic [7:0] value; + } address_mask_msb; + struct { + logic value; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] value; + } t_latency_access; + struct { + logic [7:0] value; + } rwds_sample_delay; + struct { + logic value; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] value; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] value; + } t_read_write_recovery; + struct { + logic [7:0] value; + } t_csh_cycles; + struct { + logic [7:0] value; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] value; + } value; + } rx_delay; + } chip_6; + struct { + struct { + struct { + logic [9:0] value; + } value; + } range_base; + struct { + struct { + logic [9:0] value; + } value; + } range_bound; + struct { + struct { + logic [7:0] value; + } address_space; + struct { + logic [7:0] value; + } address_mask_msb; + struct { + logic value; + } enable; + } address_cfg; + struct { + struct { + logic [7:0] value; + } t_latency_access; + struct { + logic [7:0] value; + } rwds_sample_delay; + struct { + logic value; + } en_latency_additional; + } latency_cfg; + struct { + struct { + logic [15:0] value; + } t_burst_max; + } burst_cfg; + struct { + struct { + logic [7:0] value; + } t_read_write_recovery; + struct { + logic [7:0] value; + } t_csh_cycles; + struct { + logic [7:0] value; + } csn_to_ck_cycles; + } chip_timing; + struct { + struct { + logic [7:0] value; + } value; + } rx_delay; + } chip_7; + } field_storage_t; + field_storage_t field_storage; + + assign hwif_out.global_cfg.ip_version.revision.value = 8'h0; + assign hwif_out.global_cfg.ip_version.patch.value = 8'h9; + assign hwif_out.global_cfg.ip_version.minor.value = 8'h0; + assign hwif_out.global_cfg.ip_version.major.value = 8'h0; + assign hwif_out.global_cfg.reg_if_version.minor.value = 16'h0; + assign hwif_out.global_cfg.reg_if_version.major.value = 16'h1; + // Field: hyperbus_cfg_regs.global_cfg.capability.num_chips + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.capability.num_chips.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.capability.num_chips.next; + load_next_c = '1; + field_combo.global_cfg.capability.num_chips.next = next_c; + field_combo.global_cfg.capability.num_chips.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.capability.num_chips.value <= 8'h0; + end else begin + if(field_combo.global_cfg.capability.num_chips.load_next) begin + field_storage.global_cfg.capability.num_chips.value <= field_combo.global_cfg.capability.num_chips.next; + end + end + end + assign hwif_out.global_cfg.capability.num_chips.value = field_storage.global_cfg.capability.num_chips.value; + // Field: hyperbus_cfg_regs.global_cfg.capability.num_phys + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.capability.num_phys.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.capability.num_phys.next; + load_next_c = '1; + field_combo.global_cfg.capability.num_phys.next = next_c; + field_combo.global_cfg.capability.num_phys.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.capability.num_phys.value <= 8'h0; + end else begin + if(field_combo.global_cfg.capability.num_phys.load_next) begin + field_storage.global_cfg.capability.num_phys.value <= field_combo.global_cfg.capability.num_phys.next; + end + end + end + assign hwif_out.global_cfg.capability.num_phys.value = field_storage.global_cfg.capability.num_phys.value; + // Field: hyperbus_cfg_regs.global_cfg.capability.per_chip_cfg + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.capability.per_chip_cfg.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.capability.per_chip_cfg.next; + load_next_c = '1; + field_combo.global_cfg.capability.per_chip_cfg.next = next_c; + field_combo.global_cfg.capability.per_chip_cfg.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.capability.per_chip_cfg.value <= 1'h0; + end else begin + if(field_combo.global_cfg.capability.per_chip_cfg.load_next) begin + field_storage.global_cfg.capability.per_chip_cfg.value <= field_combo.global_cfg.capability.per_chip_cfg.next; + end + end + end + assign hwif_out.global_cfg.capability.per_chip_cfg.value = field_storage.global_cfg.capability.per_chip_cfg.value; + // Field: hyperbus_cfg_regs.global_cfg.capability.per_phy_cfg + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.capability.per_phy_cfg.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.capability.per_phy_cfg.next; + load_next_c = '1; + field_combo.global_cfg.capability.per_phy_cfg.next = next_c; + field_combo.global_cfg.capability.per_phy_cfg.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.capability.per_phy_cfg.value <= 1'h0; + end else begin + if(field_combo.global_cfg.capability.per_phy_cfg.load_next) begin + field_storage.global_cfg.capability.per_phy_cfg.value <= field_combo.global_cfg.capability.per_phy_cfg.next; + end + end + end + assign hwif_out.global_cfg.capability.per_phy_cfg.value = field_storage.global_cfg.capability.per_phy_cfg.value; + // Field: hyperbus_cfg_regs.global_cfg.capability.chip_enable + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.capability.chip_enable.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.capability.chip_enable.next; + load_next_c = '1; + field_combo.global_cfg.capability.chip_enable.next = next_c; + field_combo.global_cfg.capability.chip_enable.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.capability.chip_enable.value <= 1'h0; + end else begin + if(field_combo.global_cfg.capability.chip_enable.load_next) begin + field_storage.global_cfg.capability.chip_enable.value <= field_combo.global_cfg.capability.chip_enable.next; + end + end + end + assign hwif_out.global_cfg.capability.chip_enable.value = field_storage.global_cfg.capability.chip_enable.value; + // Field: hyperbus_cfg_regs.global_cfg.capability.clock_divider + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.capability.clock_divider.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.capability.clock_divider.next; + load_next_c = '1; + field_combo.global_cfg.capability.clock_divider.next = next_c; + field_combo.global_cfg.capability.clock_divider.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.capability.clock_divider.value <= 1'h0; + end else begin + if(field_combo.global_cfg.capability.clock_divider.load_next) begin + field_storage.global_cfg.capability.clock_divider.value <= field_combo.global_cfg.capability.clock_divider.next; + end + end + end + assign hwif_out.global_cfg.capability.clock_divider.value = field_storage.global_cfg.capability.clock_divider.value; + // Field: hyperbus_cfg_regs.global_cfg.capability.staged_apply + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.capability.staged_apply.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.capability.staged_apply.next; + load_next_c = '1; + field_combo.global_cfg.capability.staged_apply.next = next_c; + field_combo.global_cfg.capability.staged_apply.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.capability.staged_apply.value <= 1'h0; + end else begin + if(field_combo.global_cfg.capability.staged_apply.load_next) begin + field_storage.global_cfg.capability.staged_apply.value <= field_combo.global_cfg.capability.staged_apply.next; + end + end + end + assign hwif_out.global_cfg.capability.staged_apply.value = field_storage.global_cfg.capability.staged_apply.value; + // Field: hyperbus_cfg_regs.global_cfg.capability.error_status + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.capability.error_status.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.capability.error_status.next; + load_next_c = '1; + field_combo.global_cfg.capability.error_status.next = next_c; + field_combo.global_cfg.capability.error_status.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.capability.error_status.value <= 1'h0; + end else begin + if(field_combo.global_cfg.capability.error_status.load_next) begin + field_storage.global_cfg.capability.error_status.value <= field_combo.global_cfg.capability.error_status.next; + end + end + end + assign hwif_out.global_cfg.capability.error_status.value = field_storage.global_cfg.capability.error_status.value; + // Field: hyperbus_cfg_regs.global_cfg.capability.rwds_sample_timing + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.capability.rwds_sample_timing.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.capability.rwds_sample_timing.next; + load_next_c = '1; + field_combo.global_cfg.capability.rwds_sample_timing.next = next_c; + field_combo.global_cfg.capability.rwds_sample_timing.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.capability.rwds_sample_timing.value <= 1'h0; + end else begin + if(field_combo.global_cfg.capability.rwds_sample_timing.load_next) begin + field_storage.global_cfg.capability.rwds_sample_timing.value <= field_combo.global_cfg.capability.rwds_sample_timing.next; + end + end + end + assign hwif_out.global_cfg.capability.rwds_sample_timing.value = field_storage.global_cfg.capability.rwds_sample_timing.value; + // Field: hyperbus_cfg_regs.global_cfg.capability.rwds_oe_timing + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.capability.rwds_oe_timing.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.capability.rwds_oe_timing.next; + load_next_c = '1; + field_combo.global_cfg.capability.rwds_oe_timing.next = next_c; + field_combo.global_cfg.capability.rwds_oe_timing.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.capability.rwds_oe_timing.value <= 1'h0; + end else begin + if(field_combo.global_cfg.capability.rwds_oe_timing.load_next) begin + field_storage.global_cfg.capability.rwds_oe_timing.value <= field_combo.global_cfg.capability.rwds_oe_timing.next; + end + end + end + assign hwif_out.global_cfg.capability.rwds_oe_timing.value = field_storage.global_cfg.capability.rwds_oe_timing.value; + // Field: hyperbus_cfg_regs.global_cfg.command.flush + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.command.flush.value; + load_next_c = '0; + if(decoded_reg_strb.global_cfg.command && decoded_req_is_wr) begin // SW write + next_c = (field_storage.global_cfg.command.flush.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]); + load_next_c = '1; + end else begin // singlepulse clears back to 0 + next_c = '0; + load_next_c = '1; + end + field_combo.global_cfg.command.flush.next = next_c; + field_combo.global_cfg.command.flush.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.command.flush.value <= 1'h0; + end else begin + if(field_combo.global_cfg.command.flush.load_next) begin + field_storage.global_cfg.command.flush.value <= field_combo.global_cfg.command.flush.next; + end + end + end + assign hwif_out.global_cfg.command.flush.value = field_storage.global_cfg.command.flush.value; + // Field: hyperbus_cfg_regs.global_cfg.command.apply + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.command.apply.value; + load_next_c = '0; + if(decoded_reg_strb.global_cfg.command && decoded_req_is_wr) begin // SW write + next_c = (field_storage.global_cfg.command.apply.value & ~decoded_wr_biten[1:1]) | (decoded_wr_data[1:1] & decoded_wr_biten[1:1]); + load_next_c = '1; + end else begin // singlepulse clears back to 0 + next_c = '0; + load_next_c = '1; + end + field_combo.global_cfg.command.apply.next = next_c; + field_combo.global_cfg.command.apply.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.command.apply.value <= 1'h0; + end else begin + if(field_combo.global_cfg.command.apply.load_next) begin + field_storage.global_cfg.command.apply.value <= field_combo.global_cfg.command.apply.next; + end + end + end + assign hwif_out.global_cfg.command.apply.value = field_storage.global_cfg.command.apply.value; + // Field: hyperbus_cfg_regs.global_cfg.status.decode_error + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.status.decode_error.value; + load_next_c = '0; + if(hwif_in.global_cfg.status.decode_error.hwset) begin // HW Set + next_c = '1; + load_next_c = '1; + end else if(decoded_reg_strb.global_cfg.status && decoded_req_is_wr) begin // SW write 1 clear + next_c = field_storage.global_cfg.status.decode_error.value & ~(decoded_wr_data[0:0] & decoded_wr_biten[0:0]); + load_next_c = '1; + end else begin // HW Write + next_c = hwif_in.global_cfg.status.decode_error.next; + load_next_c = '1; + end + field_combo.global_cfg.status.decode_error.next = next_c; + field_combo.global_cfg.status.decode_error.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.status.decode_error.value <= 1'h0; + end else begin + if(field_combo.global_cfg.status.decode_error.load_next) begin + field_storage.global_cfg.status.decode_error.value <= field_combo.global_cfg.status.decode_error.next; + end + end + end + assign hwif_out.global_cfg.status.decode_error.value = field_storage.global_cfg.status.decode_error.value; + // Field: hyperbus_cfg_regs.global_cfg.status.busy + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.status.busy.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.status.busy.next; + load_next_c = '1; + field_combo.global_cfg.status.busy.next = next_c; + field_combo.global_cfg.status.busy.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.status.busy.value <= 1'h0; + end else begin + if(field_combo.global_cfg.status.busy.load_next) begin + field_storage.global_cfg.status.busy.value <= field_combo.global_cfg.status.busy.next; + end + end + end + assign hwif_out.global_cfg.status.busy.value = field_storage.global_cfg.status.busy.value; + // Field: hyperbus_cfg_regs.global_cfg.status.dirty + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.global_cfg.status.dirty.value; + load_next_c = '0; + + // HW Write + next_c = hwif_in.global_cfg.status.dirty.next; + load_next_c = '1; + field_combo.global_cfg.status.dirty.next = next_c; + field_combo.global_cfg.status.dirty.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.global_cfg.status.dirty.value <= 1'h0; + end else begin + if(field_combo.global_cfg.status.dirty.load_next) begin + field_storage.global_cfg.status.dirty.value <= field_combo.global_cfg.status.dirty.next; + end + end + end + assign hwif_out.global_cfg.status.dirty.value = field_storage.global_cfg.status.dirty.value; + // Field: hyperbus_cfg_regs.frontend.frontend_cfg.dual_phy + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.frontend.frontend_cfg.dual_phy.value; + load_next_c = '0; + if(decoded_reg_strb.frontend.frontend_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.frontend.frontend_cfg.dual_phy.value & ~decoded_wr_biten[0:0]) | (decoded_wr_data[0:0] & decoded_wr_biten[0:0]); + load_next_c = '1; + end + field_combo.frontend.frontend_cfg.dual_phy.next = next_c; + field_combo.frontend.frontend_cfg.dual_phy.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.frontend.frontend_cfg.dual_phy.value <= 1'h1; + end else begin + if(field_combo.frontend.frontend_cfg.dual_phy.load_next) begin + field_storage.frontend.frontend_cfg.dual_phy.value <= field_combo.frontend.frontend_cfg.dual_phy.next; + end + end + end + assign hwif_out.frontend.frontend_cfg.dual_phy.value = field_storage.frontend.frontend_cfg.dual_phy.value; + // Field: hyperbus_cfg_regs.backend.clock_cfg.divider + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.backend.clock_cfg.divider.value; + load_next_c = '0; + if(decoded_reg_strb.backend.clock_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.backend.clock_cfg.divider.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.backend.clock_cfg.divider.next = next_c; + field_combo.backend.clock_cfg.divider.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.backend.clock_cfg.divider.value <= 8'h8; + end else begin + if(field_combo.backend.clock_cfg.divider.load_next) begin + field_storage.backend.clock_cfg.divider.value <= field_combo.backend.clock_cfg.divider.next; + end + end + end + assign hwif_out.backend.clock_cfg.divider.value = field_storage.backend.clock_cfg.divider.value; + // Field: hyperbus_cfg_regs.phy_0.tx_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.phy_0.tx_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.phy_0.tx_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.phy_0.tx_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.phy_0.tx_delay.value.next = next_c; + field_combo.phy_0.tx_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.phy_0.tx_delay.value.value <= 8'h10; + end else begin + if(field_combo.phy_0.tx_delay.value.load_next) begin + field_storage.phy_0.tx_delay.value.value <= field_combo.phy_0.tx_delay.value.next; + end + end + end + assign hwif_out.phy_0.tx_delay.value.value = field_storage.phy_0.tx_delay.value.value; + // Field: hyperbus_cfg_regs.phy_0.rwds_timing.rwds_oe_setup_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.phy_0.rwds_timing.rwds_oe_setup_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.phy_0.rwds_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.phy_0.rwds_timing.rwds_oe_setup_cycles.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.phy_0.rwds_timing.rwds_oe_setup_cycles.next = next_c; + field_combo.phy_0.rwds_timing.rwds_oe_setup_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.phy_0.rwds_timing.rwds_oe_setup_cycles.value <= 8'h1; + end else begin + if(field_combo.phy_0.rwds_timing.rwds_oe_setup_cycles.load_next) begin + field_storage.phy_0.rwds_timing.rwds_oe_setup_cycles.value <= field_combo.phy_0.rwds_timing.rwds_oe_setup_cycles.next; + end + end + end + assign hwif_out.phy_0.rwds_timing.rwds_oe_setup_cycles.value = field_storage.phy_0.rwds_timing.rwds_oe_setup_cycles.value; + // Field: hyperbus_cfg_regs.phy_1.tx_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.phy_1.tx_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.phy_1.tx_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.phy_1.tx_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.phy_1.tx_delay.value.next = next_c; + field_combo.phy_1.tx_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.phy_1.tx_delay.value.value <= 8'h10; + end else begin + if(field_combo.phy_1.tx_delay.value.load_next) begin + field_storage.phy_1.tx_delay.value.value <= field_combo.phy_1.tx_delay.value.next; + end + end + end + assign hwif_out.phy_1.tx_delay.value.value = field_storage.phy_1.tx_delay.value.value; + // Field: hyperbus_cfg_regs.phy_1.rwds_timing.rwds_oe_setup_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.phy_1.rwds_timing.rwds_oe_setup_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.phy_1.rwds_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.phy_1.rwds_timing.rwds_oe_setup_cycles.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.phy_1.rwds_timing.rwds_oe_setup_cycles.next = next_c; + field_combo.phy_1.rwds_timing.rwds_oe_setup_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.phy_1.rwds_timing.rwds_oe_setup_cycles.value <= 8'h1; + end else begin + if(field_combo.phy_1.rwds_timing.rwds_oe_setup_cycles.load_next) begin + field_storage.phy_1.rwds_timing.rwds_oe_setup_cycles.value <= field_combo.phy_1.rwds_timing.rwds_oe_setup_cycles.next; + end + end + end + assign hwif_out.phy_1.rwds_timing.rwds_oe_setup_cycles.value = field_storage.phy_1.rwds_timing.rwds_oe_setup_cycles.value; + // Field: hyperbus_cfg_regs.chip_0.range_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.range_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.range_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.range_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.chip_0.range_base.value.next = next_c; + field_combo.chip_0.range_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.range_base.value.value <= 10'h0; + end else begin + if(field_combo.chip_0.range_base.value.load_next) begin + field_storage.chip_0.range_base.value.value <= field_combo.chip_0.range_base.value.next; + end + end + end + assign hwif_out.chip_0.range_base.value.value = field_storage.chip_0.range_base.value.value; + // Field: hyperbus_cfg_regs.chip_0.range_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.range_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.range_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.range_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.chip_0.range_bound.value.next = next_c; + field_combo.chip_0.range_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.range_bound.value.value <= 10'h1; + end else begin + if(field_combo.chip_0.range_bound.value.load_next) begin + field_storage.chip_0.range_bound.value.value <= field_combo.chip_0.range_bound.value.next; + end + end + end + assign hwif_out.chip_0.range_bound.value.value = field_storage.chip_0.range_bound.value.value; + // Field: hyperbus_cfg_regs.chip_0.address_cfg.address_space + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.address_cfg.address_space.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.address_cfg.address_space.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_0.address_cfg.address_space.next = next_c; + field_combo.chip_0.address_cfg.address_space.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.address_cfg.address_space.value <= 8'h0; + end else begin + if(field_combo.chip_0.address_cfg.address_space.load_next) begin + field_storage.chip_0.address_cfg.address_space.value <= field_combo.chip_0.address_cfg.address_space.next; + end + end + end + assign hwif_out.chip_0.address_cfg.address_space.value = field_storage.chip_0.address_cfg.address_space.value; + // Field: hyperbus_cfg_regs.chip_0.address_cfg.address_mask_msb + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.address_cfg.address_mask_msb.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.address_cfg.address_mask_msb.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_0.address_cfg.address_mask_msb.next = next_c; + field_combo.chip_0.address_cfg.address_mask_msb.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.address_cfg.address_mask_msb.value <= 8'h19; + end else begin + if(field_combo.chip_0.address_cfg.address_mask_msb.load_next) begin + field_storage.chip_0.address_cfg.address_mask_msb.value <= field_combo.chip_0.address_cfg.address_mask_msb.next; + end + end + end + assign hwif_out.chip_0.address_cfg.address_mask_msb.value = field_storage.chip_0.address_cfg.address_mask_msb.value; + // Field: hyperbus_cfg_regs.chip_0.address_cfg.enable + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.address_cfg.enable.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.address_cfg.enable.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_0.address_cfg.enable.next = next_c; + field_combo.chip_0.address_cfg.enable.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.address_cfg.enable.value <= 1'h1; + end else begin + if(field_combo.chip_0.address_cfg.enable.load_next) begin + field_storage.chip_0.address_cfg.enable.value <= field_combo.chip_0.address_cfg.enable.next; + end + end + end + assign hwif_out.chip_0.address_cfg.enable.value = field_storage.chip_0.address_cfg.enable.value; + // Field: hyperbus_cfg_regs.chip_0.latency_cfg.t_latency_access + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.latency_cfg.t_latency_access.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.latency_cfg.t_latency_access.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_0.latency_cfg.t_latency_access.next = next_c; + field_combo.chip_0.latency_cfg.t_latency_access.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.latency_cfg.t_latency_access.value <= 8'h6; + end else begin + if(field_combo.chip_0.latency_cfg.t_latency_access.load_next) begin + field_storage.chip_0.latency_cfg.t_latency_access.value <= field_combo.chip_0.latency_cfg.t_latency_access.next; + end + end + end + assign hwif_out.chip_0.latency_cfg.t_latency_access.value = field_storage.chip_0.latency_cfg.t_latency_access.value; + // Field: hyperbus_cfg_regs.chip_0.latency_cfg.rwds_sample_delay + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.latency_cfg.rwds_sample_delay.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.latency_cfg.rwds_sample_delay.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_0.latency_cfg.rwds_sample_delay.next = next_c; + field_combo.chip_0.latency_cfg.rwds_sample_delay.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.latency_cfg.rwds_sample_delay.value <= 8'h0; + end else begin + if(field_combo.chip_0.latency_cfg.rwds_sample_delay.load_next) begin + field_storage.chip_0.latency_cfg.rwds_sample_delay.value <= field_combo.chip_0.latency_cfg.rwds_sample_delay.next; + end + end + end + assign hwif_out.chip_0.latency_cfg.rwds_sample_delay.value = field_storage.chip_0.latency_cfg.rwds_sample_delay.value; + // Field: hyperbus_cfg_regs.chip_0.latency_cfg.en_latency_additional + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.latency_cfg.en_latency_additional.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.latency_cfg.en_latency_additional.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_0.latency_cfg.en_latency_additional.next = next_c; + field_combo.chip_0.latency_cfg.en_latency_additional.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.latency_cfg.en_latency_additional.value <= 1'h0; + end else begin + if(field_combo.chip_0.latency_cfg.en_latency_additional.load_next) begin + field_storage.chip_0.latency_cfg.en_latency_additional.value <= field_combo.chip_0.latency_cfg.en_latency_additional.next; + end + end + end + assign hwif_out.chip_0.latency_cfg.en_latency_additional.value = field_storage.chip_0.latency_cfg.en_latency_additional.value; + // Field: hyperbus_cfg_regs.chip_0.burst_cfg.t_burst_max + always_comb begin + automatic logic [15:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.burst_cfg.t_burst_max.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.burst_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.burst_cfg.t_burst_max.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]); + load_next_c = '1; + end + field_combo.chip_0.burst_cfg.t_burst_max.next = next_c; + field_combo.chip_0.burst_cfg.t_burst_max.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.burst_cfg.t_burst_max.value <= 16'h15e; + end else begin + if(field_combo.chip_0.burst_cfg.t_burst_max.load_next) begin + field_storage.chip_0.burst_cfg.t_burst_max.value <= field_combo.chip_0.burst_cfg.t_burst_max.next; + end + end + end + assign hwif_out.chip_0.burst_cfg.t_burst_max.value = field_storage.chip_0.burst_cfg.t_burst_max.value; + // Field: hyperbus_cfg_regs.chip_0.chip_timing.t_read_write_recovery + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.chip_timing.t_read_write_recovery.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.chip_timing.t_read_write_recovery.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_0.chip_timing.t_read_write_recovery.next = next_c; + field_combo.chip_0.chip_timing.t_read_write_recovery.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.chip_timing.t_read_write_recovery.value <= 8'h6; + end else begin + if(field_combo.chip_0.chip_timing.t_read_write_recovery.load_next) begin + field_storage.chip_0.chip_timing.t_read_write_recovery.value <= field_combo.chip_0.chip_timing.t_read_write_recovery.next; + end + end + end + assign hwif_out.chip_0.chip_timing.t_read_write_recovery.value = field_storage.chip_0.chip_timing.t_read_write_recovery.value; + // Field: hyperbus_cfg_regs.chip_0.chip_timing.t_csh_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.chip_timing.t_csh_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.chip_timing.t_csh_cycles.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_0.chip_timing.t_csh_cycles.next = next_c; + field_combo.chip_0.chip_timing.t_csh_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.chip_timing.t_csh_cycles.value <= 8'h1; + end else begin + if(field_combo.chip_0.chip_timing.t_csh_cycles.load_next) begin + field_storage.chip_0.chip_timing.t_csh_cycles.value <= field_combo.chip_0.chip_timing.t_csh_cycles.next; + end + end + end + assign hwif_out.chip_0.chip_timing.t_csh_cycles.value = field_storage.chip_0.chip_timing.t_csh_cycles.value; + // Field: hyperbus_cfg_regs.chip_0.chip_timing.csn_to_ck_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.chip_timing.csn_to_ck_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.chip_timing.csn_to_ck_cycles.value & ~decoded_wr_biten[23:16]) | (decoded_wr_data[23:16] & decoded_wr_biten[23:16]); + load_next_c = '1; + end + field_combo.chip_0.chip_timing.csn_to_ck_cycles.next = next_c; + field_combo.chip_0.chip_timing.csn_to_ck_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.chip_timing.csn_to_ck_cycles.value <= 8'h0; + end else begin + if(field_combo.chip_0.chip_timing.csn_to_ck_cycles.load_next) begin + field_storage.chip_0.chip_timing.csn_to_ck_cycles.value <= field_combo.chip_0.chip_timing.csn_to_ck_cycles.next; + end + end + end + assign hwif_out.chip_0.chip_timing.csn_to_ck_cycles.value = field_storage.chip_0.chip_timing.csn_to_ck_cycles.value; + // Field: hyperbus_cfg_regs.chip_0.rx_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_0.rx_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_0.rx_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_0.rx_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.chip_0.rx_delay.value.next = next_c; + field_combo.chip_0.rx_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_0.rx_delay.value.value <= 8'h10; + end else begin + if(field_combo.chip_0.rx_delay.value.load_next) begin + field_storage.chip_0.rx_delay.value.value <= field_combo.chip_0.rx_delay.value.next; + end + end + end + assign hwif_out.chip_0.rx_delay.value.value = field_storage.chip_0.rx_delay.value.value; + // Field: hyperbus_cfg_regs.chip_1.range_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.range_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.range_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.range_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.chip_1.range_base.value.next = next_c; + field_combo.chip_1.range_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.range_base.value.value <= 10'h1; + end else begin + if(field_combo.chip_1.range_base.value.load_next) begin + field_storage.chip_1.range_base.value.value <= field_combo.chip_1.range_base.value.next; + end + end + end + assign hwif_out.chip_1.range_base.value.value = field_storage.chip_1.range_base.value.value; + // Field: hyperbus_cfg_regs.chip_1.range_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.range_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.range_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.range_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.chip_1.range_bound.value.next = next_c; + field_combo.chip_1.range_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.range_bound.value.value <= 10'h2; + end else begin + if(field_combo.chip_1.range_bound.value.load_next) begin + field_storage.chip_1.range_bound.value.value <= field_combo.chip_1.range_bound.value.next; + end + end + end + assign hwif_out.chip_1.range_bound.value.value = field_storage.chip_1.range_bound.value.value; + // Field: hyperbus_cfg_regs.chip_1.address_cfg.address_space + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.address_cfg.address_space.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.address_cfg.address_space.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_1.address_cfg.address_space.next = next_c; + field_combo.chip_1.address_cfg.address_space.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.address_cfg.address_space.value <= 8'h0; + end else begin + if(field_combo.chip_1.address_cfg.address_space.load_next) begin + field_storage.chip_1.address_cfg.address_space.value <= field_combo.chip_1.address_cfg.address_space.next; + end + end + end + assign hwif_out.chip_1.address_cfg.address_space.value = field_storage.chip_1.address_cfg.address_space.value; + // Field: hyperbus_cfg_regs.chip_1.address_cfg.address_mask_msb + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.address_cfg.address_mask_msb.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.address_cfg.address_mask_msb.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_1.address_cfg.address_mask_msb.next = next_c; + field_combo.chip_1.address_cfg.address_mask_msb.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.address_cfg.address_mask_msb.value <= 8'h19; + end else begin + if(field_combo.chip_1.address_cfg.address_mask_msb.load_next) begin + field_storage.chip_1.address_cfg.address_mask_msb.value <= field_combo.chip_1.address_cfg.address_mask_msb.next; + end + end + end + assign hwif_out.chip_1.address_cfg.address_mask_msb.value = field_storage.chip_1.address_cfg.address_mask_msb.value; + // Field: hyperbus_cfg_regs.chip_1.address_cfg.enable + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.address_cfg.enable.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.address_cfg.enable.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_1.address_cfg.enable.next = next_c; + field_combo.chip_1.address_cfg.enable.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.address_cfg.enable.value <= 1'h1; + end else begin + if(field_combo.chip_1.address_cfg.enable.load_next) begin + field_storage.chip_1.address_cfg.enable.value <= field_combo.chip_1.address_cfg.enable.next; + end + end + end + assign hwif_out.chip_1.address_cfg.enable.value = field_storage.chip_1.address_cfg.enable.value; + // Field: hyperbus_cfg_regs.chip_1.latency_cfg.t_latency_access + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.latency_cfg.t_latency_access.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.latency_cfg.t_latency_access.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_1.latency_cfg.t_latency_access.next = next_c; + field_combo.chip_1.latency_cfg.t_latency_access.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.latency_cfg.t_latency_access.value <= 8'h6; + end else begin + if(field_combo.chip_1.latency_cfg.t_latency_access.load_next) begin + field_storage.chip_1.latency_cfg.t_latency_access.value <= field_combo.chip_1.latency_cfg.t_latency_access.next; + end + end + end + assign hwif_out.chip_1.latency_cfg.t_latency_access.value = field_storage.chip_1.latency_cfg.t_latency_access.value; + // Field: hyperbus_cfg_regs.chip_1.latency_cfg.rwds_sample_delay + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.latency_cfg.rwds_sample_delay.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.latency_cfg.rwds_sample_delay.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_1.latency_cfg.rwds_sample_delay.next = next_c; + field_combo.chip_1.latency_cfg.rwds_sample_delay.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.latency_cfg.rwds_sample_delay.value <= 8'h0; + end else begin + if(field_combo.chip_1.latency_cfg.rwds_sample_delay.load_next) begin + field_storage.chip_1.latency_cfg.rwds_sample_delay.value <= field_combo.chip_1.latency_cfg.rwds_sample_delay.next; + end + end + end + assign hwif_out.chip_1.latency_cfg.rwds_sample_delay.value = field_storage.chip_1.latency_cfg.rwds_sample_delay.value; + // Field: hyperbus_cfg_regs.chip_1.latency_cfg.en_latency_additional + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.latency_cfg.en_latency_additional.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.latency_cfg.en_latency_additional.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_1.latency_cfg.en_latency_additional.next = next_c; + field_combo.chip_1.latency_cfg.en_latency_additional.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.latency_cfg.en_latency_additional.value <= 1'h0; + end else begin + if(field_combo.chip_1.latency_cfg.en_latency_additional.load_next) begin + field_storage.chip_1.latency_cfg.en_latency_additional.value <= field_combo.chip_1.latency_cfg.en_latency_additional.next; + end + end + end + assign hwif_out.chip_1.latency_cfg.en_latency_additional.value = field_storage.chip_1.latency_cfg.en_latency_additional.value; + // Field: hyperbus_cfg_regs.chip_1.burst_cfg.t_burst_max + always_comb begin + automatic logic [15:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.burst_cfg.t_burst_max.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.burst_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.burst_cfg.t_burst_max.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]); + load_next_c = '1; + end + field_combo.chip_1.burst_cfg.t_burst_max.next = next_c; + field_combo.chip_1.burst_cfg.t_burst_max.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.burst_cfg.t_burst_max.value <= 16'h15e; + end else begin + if(field_combo.chip_1.burst_cfg.t_burst_max.load_next) begin + field_storage.chip_1.burst_cfg.t_burst_max.value <= field_combo.chip_1.burst_cfg.t_burst_max.next; + end + end + end + assign hwif_out.chip_1.burst_cfg.t_burst_max.value = field_storage.chip_1.burst_cfg.t_burst_max.value; + // Field: hyperbus_cfg_regs.chip_1.chip_timing.t_read_write_recovery + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.chip_timing.t_read_write_recovery.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.chip_timing.t_read_write_recovery.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_1.chip_timing.t_read_write_recovery.next = next_c; + field_combo.chip_1.chip_timing.t_read_write_recovery.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.chip_timing.t_read_write_recovery.value <= 8'h6; + end else begin + if(field_combo.chip_1.chip_timing.t_read_write_recovery.load_next) begin + field_storage.chip_1.chip_timing.t_read_write_recovery.value <= field_combo.chip_1.chip_timing.t_read_write_recovery.next; + end + end + end + assign hwif_out.chip_1.chip_timing.t_read_write_recovery.value = field_storage.chip_1.chip_timing.t_read_write_recovery.value; + // Field: hyperbus_cfg_regs.chip_1.chip_timing.t_csh_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.chip_timing.t_csh_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.chip_timing.t_csh_cycles.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_1.chip_timing.t_csh_cycles.next = next_c; + field_combo.chip_1.chip_timing.t_csh_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.chip_timing.t_csh_cycles.value <= 8'h1; + end else begin + if(field_combo.chip_1.chip_timing.t_csh_cycles.load_next) begin + field_storage.chip_1.chip_timing.t_csh_cycles.value <= field_combo.chip_1.chip_timing.t_csh_cycles.next; + end + end + end + assign hwif_out.chip_1.chip_timing.t_csh_cycles.value = field_storage.chip_1.chip_timing.t_csh_cycles.value; + // Field: hyperbus_cfg_regs.chip_1.chip_timing.csn_to_ck_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.chip_timing.csn_to_ck_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.chip_timing.csn_to_ck_cycles.value & ~decoded_wr_biten[23:16]) | (decoded_wr_data[23:16] & decoded_wr_biten[23:16]); + load_next_c = '1; + end + field_combo.chip_1.chip_timing.csn_to_ck_cycles.next = next_c; + field_combo.chip_1.chip_timing.csn_to_ck_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.chip_timing.csn_to_ck_cycles.value <= 8'h0; + end else begin + if(field_combo.chip_1.chip_timing.csn_to_ck_cycles.load_next) begin + field_storage.chip_1.chip_timing.csn_to_ck_cycles.value <= field_combo.chip_1.chip_timing.csn_to_ck_cycles.next; + end + end + end + assign hwif_out.chip_1.chip_timing.csn_to_ck_cycles.value = field_storage.chip_1.chip_timing.csn_to_ck_cycles.value; + // Field: hyperbus_cfg_regs.chip_1.rx_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_1.rx_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_1.rx_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_1.rx_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.chip_1.rx_delay.value.next = next_c; + field_combo.chip_1.rx_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_1.rx_delay.value.value <= 8'h10; + end else begin + if(field_combo.chip_1.rx_delay.value.load_next) begin + field_storage.chip_1.rx_delay.value.value <= field_combo.chip_1.rx_delay.value.next; + end + end + end + assign hwif_out.chip_1.rx_delay.value.value = field_storage.chip_1.rx_delay.value.value; + // Field: hyperbus_cfg_regs.chip_2.range_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.range_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.range_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.range_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.chip_2.range_base.value.next = next_c; + field_combo.chip_2.range_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.range_base.value.value <= 10'h2; + end else begin + if(field_combo.chip_2.range_base.value.load_next) begin + field_storage.chip_2.range_base.value.value <= field_combo.chip_2.range_base.value.next; + end + end + end + assign hwif_out.chip_2.range_base.value.value = field_storage.chip_2.range_base.value.value; + // Field: hyperbus_cfg_regs.chip_2.range_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.range_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.range_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.range_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.chip_2.range_bound.value.next = next_c; + field_combo.chip_2.range_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.range_bound.value.value <= 10'h3; + end else begin + if(field_combo.chip_2.range_bound.value.load_next) begin + field_storage.chip_2.range_bound.value.value <= field_combo.chip_2.range_bound.value.next; + end + end + end + assign hwif_out.chip_2.range_bound.value.value = field_storage.chip_2.range_bound.value.value; + // Field: hyperbus_cfg_regs.chip_2.address_cfg.address_space + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.address_cfg.address_space.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.address_cfg.address_space.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_2.address_cfg.address_space.next = next_c; + field_combo.chip_2.address_cfg.address_space.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.address_cfg.address_space.value <= 8'h0; + end else begin + if(field_combo.chip_2.address_cfg.address_space.load_next) begin + field_storage.chip_2.address_cfg.address_space.value <= field_combo.chip_2.address_cfg.address_space.next; + end + end + end + assign hwif_out.chip_2.address_cfg.address_space.value = field_storage.chip_2.address_cfg.address_space.value; + // Field: hyperbus_cfg_regs.chip_2.address_cfg.address_mask_msb + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.address_cfg.address_mask_msb.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.address_cfg.address_mask_msb.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_2.address_cfg.address_mask_msb.next = next_c; + field_combo.chip_2.address_cfg.address_mask_msb.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.address_cfg.address_mask_msb.value <= 8'h19; + end else begin + if(field_combo.chip_2.address_cfg.address_mask_msb.load_next) begin + field_storage.chip_2.address_cfg.address_mask_msb.value <= field_combo.chip_2.address_cfg.address_mask_msb.next; + end + end + end + assign hwif_out.chip_2.address_cfg.address_mask_msb.value = field_storage.chip_2.address_cfg.address_mask_msb.value; + // Field: hyperbus_cfg_regs.chip_2.address_cfg.enable + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.address_cfg.enable.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.address_cfg.enable.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_2.address_cfg.enable.next = next_c; + field_combo.chip_2.address_cfg.enable.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.address_cfg.enable.value <= 1'h1; + end else begin + if(field_combo.chip_2.address_cfg.enable.load_next) begin + field_storage.chip_2.address_cfg.enable.value <= field_combo.chip_2.address_cfg.enable.next; + end + end + end + assign hwif_out.chip_2.address_cfg.enable.value = field_storage.chip_2.address_cfg.enable.value; + // Field: hyperbus_cfg_regs.chip_2.latency_cfg.t_latency_access + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.latency_cfg.t_latency_access.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.latency_cfg.t_latency_access.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_2.latency_cfg.t_latency_access.next = next_c; + field_combo.chip_2.latency_cfg.t_latency_access.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.latency_cfg.t_latency_access.value <= 8'h6; + end else begin + if(field_combo.chip_2.latency_cfg.t_latency_access.load_next) begin + field_storage.chip_2.latency_cfg.t_latency_access.value <= field_combo.chip_2.latency_cfg.t_latency_access.next; + end + end + end + assign hwif_out.chip_2.latency_cfg.t_latency_access.value = field_storage.chip_2.latency_cfg.t_latency_access.value; + // Field: hyperbus_cfg_regs.chip_2.latency_cfg.rwds_sample_delay + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.latency_cfg.rwds_sample_delay.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.latency_cfg.rwds_sample_delay.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_2.latency_cfg.rwds_sample_delay.next = next_c; + field_combo.chip_2.latency_cfg.rwds_sample_delay.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.latency_cfg.rwds_sample_delay.value <= 8'h0; + end else begin + if(field_combo.chip_2.latency_cfg.rwds_sample_delay.load_next) begin + field_storage.chip_2.latency_cfg.rwds_sample_delay.value <= field_combo.chip_2.latency_cfg.rwds_sample_delay.next; + end + end + end + assign hwif_out.chip_2.latency_cfg.rwds_sample_delay.value = field_storage.chip_2.latency_cfg.rwds_sample_delay.value; + // Field: hyperbus_cfg_regs.chip_2.latency_cfg.en_latency_additional + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.latency_cfg.en_latency_additional.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.latency_cfg.en_latency_additional.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_2.latency_cfg.en_latency_additional.next = next_c; + field_combo.chip_2.latency_cfg.en_latency_additional.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.latency_cfg.en_latency_additional.value <= 1'h0; + end else begin + if(field_combo.chip_2.latency_cfg.en_latency_additional.load_next) begin + field_storage.chip_2.latency_cfg.en_latency_additional.value <= field_combo.chip_2.latency_cfg.en_latency_additional.next; + end + end + end + assign hwif_out.chip_2.latency_cfg.en_latency_additional.value = field_storage.chip_2.latency_cfg.en_latency_additional.value; + // Field: hyperbus_cfg_regs.chip_2.burst_cfg.t_burst_max + always_comb begin + automatic logic [15:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.burst_cfg.t_burst_max.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.burst_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.burst_cfg.t_burst_max.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]); + load_next_c = '1; + end + field_combo.chip_2.burst_cfg.t_burst_max.next = next_c; + field_combo.chip_2.burst_cfg.t_burst_max.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.burst_cfg.t_burst_max.value <= 16'h15e; + end else begin + if(field_combo.chip_2.burst_cfg.t_burst_max.load_next) begin + field_storage.chip_2.burst_cfg.t_burst_max.value <= field_combo.chip_2.burst_cfg.t_burst_max.next; + end + end + end + assign hwif_out.chip_2.burst_cfg.t_burst_max.value = field_storage.chip_2.burst_cfg.t_burst_max.value; + // Field: hyperbus_cfg_regs.chip_2.chip_timing.t_read_write_recovery + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.chip_timing.t_read_write_recovery.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.chip_timing.t_read_write_recovery.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_2.chip_timing.t_read_write_recovery.next = next_c; + field_combo.chip_2.chip_timing.t_read_write_recovery.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.chip_timing.t_read_write_recovery.value <= 8'h6; + end else begin + if(field_combo.chip_2.chip_timing.t_read_write_recovery.load_next) begin + field_storage.chip_2.chip_timing.t_read_write_recovery.value <= field_combo.chip_2.chip_timing.t_read_write_recovery.next; + end + end + end + assign hwif_out.chip_2.chip_timing.t_read_write_recovery.value = field_storage.chip_2.chip_timing.t_read_write_recovery.value; + // Field: hyperbus_cfg_regs.chip_2.chip_timing.t_csh_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.chip_timing.t_csh_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.chip_timing.t_csh_cycles.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_2.chip_timing.t_csh_cycles.next = next_c; + field_combo.chip_2.chip_timing.t_csh_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.chip_timing.t_csh_cycles.value <= 8'h1; + end else begin + if(field_combo.chip_2.chip_timing.t_csh_cycles.load_next) begin + field_storage.chip_2.chip_timing.t_csh_cycles.value <= field_combo.chip_2.chip_timing.t_csh_cycles.next; + end + end + end + assign hwif_out.chip_2.chip_timing.t_csh_cycles.value = field_storage.chip_2.chip_timing.t_csh_cycles.value; + // Field: hyperbus_cfg_regs.chip_2.chip_timing.csn_to_ck_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.chip_timing.csn_to_ck_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.chip_timing.csn_to_ck_cycles.value & ~decoded_wr_biten[23:16]) | (decoded_wr_data[23:16] & decoded_wr_biten[23:16]); + load_next_c = '1; + end + field_combo.chip_2.chip_timing.csn_to_ck_cycles.next = next_c; + field_combo.chip_2.chip_timing.csn_to_ck_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.chip_timing.csn_to_ck_cycles.value <= 8'h0; + end else begin + if(field_combo.chip_2.chip_timing.csn_to_ck_cycles.load_next) begin + field_storage.chip_2.chip_timing.csn_to_ck_cycles.value <= field_combo.chip_2.chip_timing.csn_to_ck_cycles.next; + end + end + end + assign hwif_out.chip_2.chip_timing.csn_to_ck_cycles.value = field_storage.chip_2.chip_timing.csn_to_ck_cycles.value; + // Field: hyperbus_cfg_regs.chip_2.rx_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_2.rx_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_2.rx_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_2.rx_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.chip_2.rx_delay.value.next = next_c; + field_combo.chip_2.rx_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_2.rx_delay.value.value <= 8'h10; + end else begin + if(field_combo.chip_2.rx_delay.value.load_next) begin + field_storage.chip_2.rx_delay.value.value <= field_combo.chip_2.rx_delay.value.next; + end + end + end + assign hwif_out.chip_2.rx_delay.value.value = field_storage.chip_2.rx_delay.value.value; + // Field: hyperbus_cfg_regs.chip_3.range_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.range_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.range_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.range_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.chip_3.range_base.value.next = next_c; + field_combo.chip_3.range_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.range_base.value.value <= 10'h3; + end else begin + if(field_combo.chip_3.range_base.value.load_next) begin + field_storage.chip_3.range_base.value.value <= field_combo.chip_3.range_base.value.next; + end + end + end + assign hwif_out.chip_3.range_base.value.value = field_storage.chip_3.range_base.value.value; + // Field: hyperbus_cfg_regs.chip_3.range_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.range_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.range_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.range_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.chip_3.range_bound.value.next = next_c; + field_combo.chip_3.range_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.range_bound.value.value <= 10'h4; + end else begin + if(field_combo.chip_3.range_bound.value.load_next) begin + field_storage.chip_3.range_bound.value.value <= field_combo.chip_3.range_bound.value.next; + end + end + end + assign hwif_out.chip_3.range_bound.value.value = field_storage.chip_3.range_bound.value.value; + // Field: hyperbus_cfg_regs.chip_3.address_cfg.address_space + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.address_cfg.address_space.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.address_cfg.address_space.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_3.address_cfg.address_space.next = next_c; + field_combo.chip_3.address_cfg.address_space.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.address_cfg.address_space.value <= 8'h0; + end else begin + if(field_combo.chip_3.address_cfg.address_space.load_next) begin + field_storage.chip_3.address_cfg.address_space.value <= field_combo.chip_3.address_cfg.address_space.next; + end + end + end + assign hwif_out.chip_3.address_cfg.address_space.value = field_storage.chip_3.address_cfg.address_space.value; + // Field: hyperbus_cfg_regs.chip_3.address_cfg.address_mask_msb + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.address_cfg.address_mask_msb.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.address_cfg.address_mask_msb.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_3.address_cfg.address_mask_msb.next = next_c; + field_combo.chip_3.address_cfg.address_mask_msb.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.address_cfg.address_mask_msb.value <= 8'h19; + end else begin + if(field_combo.chip_3.address_cfg.address_mask_msb.load_next) begin + field_storage.chip_3.address_cfg.address_mask_msb.value <= field_combo.chip_3.address_cfg.address_mask_msb.next; + end + end + end + assign hwif_out.chip_3.address_cfg.address_mask_msb.value = field_storage.chip_3.address_cfg.address_mask_msb.value; + // Field: hyperbus_cfg_regs.chip_3.address_cfg.enable + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.address_cfg.enable.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.address_cfg.enable.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_3.address_cfg.enable.next = next_c; + field_combo.chip_3.address_cfg.enable.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.address_cfg.enable.value <= 1'h1; + end else begin + if(field_combo.chip_3.address_cfg.enable.load_next) begin + field_storage.chip_3.address_cfg.enable.value <= field_combo.chip_3.address_cfg.enable.next; + end + end + end + assign hwif_out.chip_3.address_cfg.enable.value = field_storage.chip_3.address_cfg.enable.value; + // Field: hyperbus_cfg_regs.chip_3.latency_cfg.t_latency_access + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.latency_cfg.t_latency_access.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.latency_cfg.t_latency_access.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_3.latency_cfg.t_latency_access.next = next_c; + field_combo.chip_3.latency_cfg.t_latency_access.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.latency_cfg.t_latency_access.value <= 8'h6; + end else begin + if(field_combo.chip_3.latency_cfg.t_latency_access.load_next) begin + field_storage.chip_3.latency_cfg.t_latency_access.value <= field_combo.chip_3.latency_cfg.t_latency_access.next; + end + end + end + assign hwif_out.chip_3.latency_cfg.t_latency_access.value = field_storage.chip_3.latency_cfg.t_latency_access.value; + // Field: hyperbus_cfg_regs.chip_3.latency_cfg.rwds_sample_delay + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.latency_cfg.rwds_sample_delay.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.latency_cfg.rwds_sample_delay.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_3.latency_cfg.rwds_sample_delay.next = next_c; + field_combo.chip_3.latency_cfg.rwds_sample_delay.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.latency_cfg.rwds_sample_delay.value <= 8'h0; + end else begin + if(field_combo.chip_3.latency_cfg.rwds_sample_delay.load_next) begin + field_storage.chip_3.latency_cfg.rwds_sample_delay.value <= field_combo.chip_3.latency_cfg.rwds_sample_delay.next; + end + end + end + assign hwif_out.chip_3.latency_cfg.rwds_sample_delay.value = field_storage.chip_3.latency_cfg.rwds_sample_delay.value; + // Field: hyperbus_cfg_regs.chip_3.latency_cfg.en_latency_additional + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.latency_cfg.en_latency_additional.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.latency_cfg.en_latency_additional.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_3.latency_cfg.en_latency_additional.next = next_c; + field_combo.chip_3.latency_cfg.en_latency_additional.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.latency_cfg.en_latency_additional.value <= 1'h0; + end else begin + if(field_combo.chip_3.latency_cfg.en_latency_additional.load_next) begin + field_storage.chip_3.latency_cfg.en_latency_additional.value <= field_combo.chip_3.latency_cfg.en_latency_additional.next; + end + end + end + assign hwif_out.chip_3.latency_cfg.en_latency_additional.value = field_storage.chip_3.latency_cfg.en_latency_additional.value; + // Field: hyperbus_cfg_regs.chip_3.burst_cfg.t_burst_max + always_comb begin + automatic logic [15:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.burst_cfg.t_burst_max.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.burst_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.burst_cfg.t_burst_max.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]); + load_next_c = '1; + end + field_combo.chip_3.burst_cfg.t_burst_max.next = next_c; + field_combo.chip_3.burst_cfg.t_burst_max.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.burst_cfg.t_burst_max.value <= 16'h15e; + end else begin + if(field_combo.chip_3.burst_cfg.t_burst_max.load_next) begin + field_storage.chip_3.burst_cfg.t_burst_max.value <= field_combo.chip_3.burst_cfg.t_burst_max.next; + end + end + end + assign hwif_out.chip_3.burst_cfg.t_burst_max.value = field_storage.chip_3.burst_cfg.t_burst_max.value; + // Field: hyperbus_cfg_regs.chip_3.chip_timing.t_read_write_recovery + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.chip_timing.t_read_write_recovery.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.chip_timing.t_read_write_recovery.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_3.chip_timing.t_read_write_recovery.next = next_c; + field_combo.chip_3.chip_timing.t_read_write_recovery.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.chip_timing.t_read_write_recovery.value <= 8'h6; + end else begin + if(field_combo.chip_3.chip_timing.t_read_write_recovery.load_next) begin + field_storage.chip_3.chip_timing.t_read_write_recovery.value <= field_combo.chip_3.chip_timing.t_read_write_recovery.next; + end + end + end + assign hwif_out.chip_3.chip_timing.t_read_write_recovery.value = field_storage.chip_3.chip_timing.t_read_write_recovery.value; + // Field: hyperbus_cfg_regs.chip_3.chip_timing.t_csh_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.chip_timing.t_csh_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.chip_timing.t_csh_cycles.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_3.chip_timing.t_csh_cycles.next = next_c; + field_combo.chip_3.chip_timing.t_csh_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.chip_timing.t_csh_cycles.value <= 8'h1; + end else begin + if(field_combo.chip_3.chip_timing.t_csh_cycles.load_next) begin + field_storage.chip_3.chip_timing.t_csh_cycles.value <= field_combo.chip_3.chip_timing.t_csh_cycles.next; + end + end + end + assign hwif_out.chip_3.chip_timing.t_csh_cycles.value = field_storage.chip_3.chip_timing.t_csh_cycles.value; + // Field: hyperbus_cfg_regs.chip_3.chip_timing.csn_to_ck_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.chip_timing.csn_to_ck_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.chip_timing.csn_to_ck_cycles.value & ~decoded_wr_biten[23:16]) | (decoded_wr_data[23:16] & decoded_wr_biten[23:16]); + load_next_c = '1; + end + field_combo.chip_3.chip_timing.csn_to_ck_cycles.next = next_c; + field_combo.chip_3.chip_timing.csn_to_ck_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.chip_timing.csn_to_ck_cycles.value <= 8'h0; + end else begin + if(field_combo.chip_3.chip_timing.csn_to_ck_cycles.load_next) begin + field_storage.chip_3.chip_timing.csn_to_ck_cycles.value <= field_combo.chip_3.chip_timing.csn_to_ck_cycles.next; + end + end + end + assign hwif_out.chip_3.chip_timing.csn_to_ck_cycles.value = field_storage.chip_3.chip_timing.csn_to_ck_cycles.value; + // Field: hyperbus_cfg_regs.chip_3.rx_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_3.rx_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_3.rx_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_3.rx_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.chip_3.rx_delay.value.next = next_c; + field_combo.chip_3.rx_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_3.rx_delay.value.value <= 8'h10; + end else begin + if(field_combo.chip_3.rx_delay.value.load_next) begin + field_storage.chip_3.rx_delay.value.value <= field_combo.chip_3.rx_delay.value.next; + end + end + end + assign hwif_out.chip_3.rx_delay.value.value = field_storage.chip_3.rx_delay.value.value; + // Field: hyperbus_cfg_regs.chip_4.range_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.range_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.range_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.range_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.chip_4.range_base.value.next = next_c; + field_combo.chip_4.range_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.range_base.value.value <= 10'h4; + end else begin + if(field_combo.chip_4.range_base.value.load_next) begin + field_storage.chip_4.range_base.value.value <= field_combo.chip_4.range_base.value.next; + end + end + end + assign hwif_out.chip_4.range_base.value.value = field_storage.chip_4.range_base.value.value; + // Field: hyperbus_cfg_regs.chip_4.range_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.range_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.range_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.range_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.chip_4.range_bound.value.next = next_c; + field_combo.chip_4.range_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.range_bound.value.value <= 10'h5; + end else begin + if(field_combo.chip_4.range_bound.value.load_next) begin + field_storage.chip_4.range_bound.value.value <= field_combo.chip_4.range_bound.value.next; + end + end + end + assign hwif_out.chip_4.range_bound.value.value = field_storage.chip_4.range_bound.value.value; + // Field: hyperbus_cfg_regs.chip_4.address_cfg.address_space + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.address_cfg.address_space.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.address_cfg.address_space.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_4.address_cfg.address_space.next = next_c; + field_combo.chip_4.address_cfg.address_space.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.address_cfg.address_space.value <= 8'h0; + end else begin + if(field_combo.chip_4.address_cfg.address_space.load_next) begin + field_storage.chip_4.address_cfg.address_space.value <= field_combo.chip_4.address_cfg.address_space.next; + end + end + end + assign hwif_out.chip_4.address_cfg.address_space.value = field_storage.chip_4.address_cfg.address_space.value; + // Field: hyperbus_cfg_regs.chip_4.address_cfg.address_mask_msb + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.address_cfg.address_mask_msb.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.address_cfg.address_mask_msb.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_4.address_cfg.address_mask_msb.next = next_c; + field_combo.chip_4.address_cfg.address_mask_msb.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.address_cfg.address_mask_msb.value <= 8'h19; + end else begin + if(field_combo.chip_4.address_cfg.address_mask_msb.load_next) begin + field_storage.chip_4.address_cfg.address_mask_msb.value <= field_combo.chip_4.address_cfg.address_mask_msb.next; + end + end + end + assign hwif_out.chip_4.address_cfg.address_mask_msb.value = field_storage.chip_4.address_cfg.address_mask_msb.value; + // Field: hyperbus_cfg_regs.chip_4.address_cfg.enable + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.address_cfg.enable.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.address_cfg.enable.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_4.address_cfg.enable.next = next_c; + field_combo.chip_4.address_cfg.enable.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.address_cfg.enable.value <= 1'h1; + end else begin + if(field_combo.chip_4.address_cfg.enable.load_next) begin + field_storage.chip_4.address_cfg.enable.value <= field_combo.chip_4.address_cfg.enable.next; + end + end + end + assign hwif_out.chip_4.address_cfg.enable.value = field_storage.chip_4.address_cfg.enable.value; + // Field: hyperbus_cfg_regs.chip_4.latency_cfg.t_latency_access + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.latency_cfg.t_latency_access.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.latency_cfg.t_latency_access.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_4.latency_cfg.t_latency_access.next = next_c; + field_combo.chip_4.latency_cfg.t_latency_access.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.latency_cfg.t_latency_access.value <= 8'h6; + end else begin + if(field_combo.chip_4.latency_cfg.t_latency_access.load_next) begin + field_storage.chip_4.latency_cfg.t_latency_access.value <= field_combo.chip_4.latency_cfg.t_latency_access.next; + end + end + end + assign hwif_out.chip_4.latency_cfg.t_latency_access.value = field_storage.chip_4.latency_cfg.t_latency_access.value; + // Field: hyperbus_cfg_regs.chip_4.latency_cfg.rwds_sample_delay + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.latency_cfg.rwds_sample_delay.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.latency_cfg.rwds_sample_delay.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_4.latency_cfg.rwds_sample_delay.next = next_c; + field_combo.chip_4.latency_cfg.rwds_sample_delay.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.latency_cfg.rwds_sample_delay.value <= 8'h0; + end else begin + if(field_combo.chip_4.latency_cfg.rwds_sample_delay.load_next) begin + field_storage.chip_4.latency_cfg.rwds_sample_delay.value <= field_combo.chip_4.latency_cfg.rwds_sample_delay.next; + end + end + end + assign hwif_out.chip_4.latency_cfg.rwds_sample_delay.value = field_storage.chip_4.latency_cfg.rwds_sample_delay.value; + // Field: hyperbus_cfg_regs.chip_4.latency_cfg.en_latency_additional + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.latency_cfg.en_latency_additional.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.latency_cfg.en_latency_additional.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_4.latency_cfg.en_latency_additional.next = next_c; + field_combo.chip_4.latency_cfg.en_latency_additional.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.latency_cfg.en_latency_additional.value <= 1'h0; + end else begin + if(field_combo.chip_4.latency_cfg.en_latency_additional.load_next) begin + field_storage.chip_4.latency_cfg.en_latency_additional.value <= field_combo.chip_4.latency_cfg.en_latency_additional.next; + end + end + end + assign hwif_out.chip_4.latency_cfg.en_latency_additional.value = field_storage.chip_4.latency_cfg.en_latency_additional.value; + // Field: hyperbus_cfg_regs.chip_4.burst_cfg.t_burst_max + always_comb begin + automatic logic [15:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.burst_cfg.t_burst_max.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.burst_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.burst_cfg.t_burst_max.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]); + load_next_c = '1; + end + field_combo.chip_4.burst_cfg.t_burst_max.next = next_c; + field_combo.chip_4.burst_cfg.t_burst_max.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.burst_cfg.t_burst_max.value <= 16'h15e; + end else begin + if(field_combo.chip_4.burst_cfg.t_burst_max.load_next) begin + field_storage.chip_4.burst_cfg.t_burst_max.value <= field_combo.chip_4.burst_cfg.t_burst_max.next; + end + end + end + assign hwif_out.chip_4.burst_cfg.t_burst_max.value = field_storage.chip_4.burst_cfg.t_burst_max.value; + // Field: hyperbus_cfg_regs.chip_4.chip_timing.t_read_write_recovery + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.chip_timing.t_read_write_recovery.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.chip_timing.t_read_write_recovery.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_4.chip_timing.t_read_write_recovery.next = next_c; + field_combo.chip_4.chip_timing.t_read_write_recovery.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.chip_timing.t_read_write_recovery.value <= 8'h6; + end else begin + if(field_combo.chip_4.chip_timing.t_read_write_recovery.load_next) begin + field_storage.chip_4.chip_timing.t_read_write_recovery.value <= field_combo.chip_4.chip_timing.t_read_write_recovery.next; + end + end + end + assign hwif_out.chip_4.chip_timing.t_read_write_recovery.value = field_storage.chip_4.chip_timing.t_read_write_recovery.value; + // Field: hyperbus_cfg_regs.chip_4.chip_timing.t_csh_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.chip_timing.t_csh_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.chip_timing.t_csh_cycles.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_4.chip_timing.t_csh_cycles.next = next_c; + field_combo.chip_4.chip_timing.t_csh_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.chip_timing.t_csh_cycles.value <= 8'h1; + end else begin + if(field_combo.chip_4.chip_timing.t_csh_cycles.load_next) begin + field_storage.chip_4.chip_timing.t_csh_cycles.value <= field_combo.chip_4.chip_timing.t_csh_cycles.next; + end + end + end + assign hwif_out.chip_4.chip_timing.t_csh_cycles.value = field_storage.chip_4.chip_timing.t_csh_cycles.value; + // Field: hyperbus_cfg_regs.chip_4.chip_timing.csn_to_ck_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.chip_timing.csn_to_ck_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.chip_timing.csn_to_ck_cycles.value & ~decoded_wr_biten[23:16]) | (decoded_wr_data[23:16] & decoded_wr_biten[23:16]); + load_next_c = '1; + end + field_combo.chip_4.chip_timing.csn_to_ck_cycles.next = next_c; + field_combo.chip_4.chip_timing.csn_to_ck_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.chip_timing.csn_to_ck_cycles.value <= 8'h0; + end else begin + if(field_combo.chip_4.chip_timing.csn_to_ck_cycles.load_next) begin + field_storage.chip_4.chip_timing.csn_to_ck_cycles.value <= field_combo.chip_4.chip_timing.csn_to_ck_cycles.next; + end + end + end + assign hwif_out.chip_4.chip_timing.csn_to_ck_cycles.value = field_storage.chip_4.chip_timing.csn_to_ck_cycles.value; + // Field: hyperbus_cfg_regs.chip_4.rx_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_4.rx_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_4.rx_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_4.rx_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.chip_4.rx_delay.value.next = next_c; + field_combo.chip_4.rx_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_4.rx_delay.value.value <= 8'h10; + end else begin + if(field_combo.chip_4.rx_delay.value.load_next) begin + field_storage.chip_4.rx_delay.value.value <= field_combo.chip_4.rx_delay.value.next; + end + end + end + assign hwif_out.chip_4.rx_delay.value.value = field_storage.chip_4.rx_delay.value.value; + // Field: hyperbus_cfg_regs.chip_5.range_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.range_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.range_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.range_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.chip_5.range_base.value.next = next_c; + field_combo.chip_5.range_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.range_base.value.value <= 10'h5; + end else begin + if(field_combo.chip_5.range_base.value.load_next) begin + field_storage.chip_5.range_base.value.value <= field_combo.chip_5.range_base.value.next; + end + end + end + assign hwif_out.chip_5.range_base.value.value = field_storage.chip_5.range_base.value.value; + // Field: hyperbus_cfg_regs.chip_5.range_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.range_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.range_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.range_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.chip_5.range_bound.value.next = next_c; + field_combo.chip_5.range_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.range_bound.value.value <= 10'h6; + end else begin + if(field_combo.chip_5.range_bound.value.load_next) begin + field_storage.chip_5.range_bound.value.value <= field_combo.chip_5.range_bound.value.next; + end + end + end + assign hwif_out.chip_5.range_bound.value.value = field_storage.chip_5.range_bound.value.value; + // Field: hyperbus_cfg_regs.chip_5.address_cfg.address_space + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.address_cfg.address_space.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.address_cfg.address_space.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_5.address_cfg.address_space.next = next_c; + field_combo.chip_5.address_cfg.address_space.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.address_cfg.address_space.value <= 8'h0; + end else begin + if(field_combo.chip_5.address_cfg.address_space.load_next) begin + field_storage.chip_5.address_cfg.address_space.value <= field_combo.chip_5.address_cfg.address_space.next; + end + end + end + assign hwif_out.chip_5.address_cfg.address_space.value = field_storage.chip_5.address_cfg.address_space.value; + // Field: hyperbus_cfg_regs.chip_5.address_cfg.address_mask_msb + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.address_cfg.address_mask_msb.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.address_cfg.address_mask_msb.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_5.address_cfg.address_mask_msb.next = next_c; + field_combo.chip_5.address_cfg.address_mask_msb.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.address_cfg.address_mask_msb.value <= 8'h19; + end else begin + if(field_combo.chip_5.address_cfg.address_mask_msb.load_next) begin + field_storage.chip_5.address_cfg.address_mask_msb.value <= field_combo.chip_5.address_cfg.address_mask_msb.next; + end + end + end + assign hwif_out.chip_5.address_cfg.address_mask_msb.value = field_storage.chip_5.address_cfg.address_mask_msb.value; + // Field: hyperbus_cfg_regs.chip_5.address_cfg.enable + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.address_cfg.enable.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.address_cfg.enable.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_5.address_cfg.enable.next = next_c; + field_combo.chip_5.address_cfg.enable.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.address_cfg.enable.value <= 1'h1; + end else begin + if(field_combo.chip_5.address_cfg.enable.load_next) begin + field_storage.chip_5.address_cfg.enable.value <= field_combo.chip_5.address_cfg.enable.next; + end + end + end + assign hwif_out.chip_5.address_cfg.enable.value = field_storage.chip_5.address_cfg.enable.value; + // Field: hyperbus_cfg_regs.chip_5.latency_cfg.t_latency_access + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.latency_cfg.t_latency_access.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.latency_cfg.t_latency_access.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_5.latency_cfg.t_latency_access.next = next_c; + field_combo.chip_5.latency_cfg.t_latency_access.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.latency_cfg.t_latency_access.value <= 8'h6; + end else begin + if(field_combo.chip_5.latency_cfg.t_latency_access.load_next) begin + field_storage.chip_5.latency_cfg.t_latency_access.value <= field_combo.chip_5.latency_cfg.t_latency_access.next; + end + end + end + assign hwif_out.chip_5.latency_cfg.t_latency_access.value = field_storage.chip_5.latency_cfg.t_latency_access.value; + // Field: hyperbus_cfg_regs.chip_5.latency_cfg.rwds_sample_delay + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.latency_cfg.rwds_sample_delay.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.latency_cfg.rwds_sample_delay.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_5.latency_cfg.rwds_sample_delay.next = next_c; + field_combo.chip_5.latency_cfg.rwds_sample_delay.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.latency_cfg.rwds_sample_delay.value <= 8'h0; + end else begin + if(field_combo.chip_5.latency_cfg.rwds_sample_delay.load_next) begin + field_storage.chip_5.latency_cfg.rwds_sample_delay.value <= field_combo.chip_5.latency_cfg.rwds_sample_delay.next; + end + end + end + assign hwif_out.chip_5.latency_cfg.rwds_sample_delay.value = field_storage.chip_5.latency_cfg.rwds_sample_delay.value; + // Field: hyperbus_cfg_regs.chip_5.latency_cfg.en_latency_additional + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.latency_cfg.en_latency_additional.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.latency_cfg.en_latency_additional.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_5.latency_cfg.en_latency_additional.next = next_c; + field_combo.chip_5.latency_cfg.en_latency_additional.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.latency_cfg.en_latency_additional.value <= 1'h0; + end else begin + if(field_combo.chip_5.latency_cfg.en_latency_additional.load_next) begin + field_storage.chip_5.latency_cfg.en_latency_additional.value <= field_combo.chip_5.latency_cfg.en_latency_additional.next; + end + end + end + assign hwif_out.chip_5.latency_cfg.en_latency_additional.value = field_storage.chip_5.latency_cfg.en_latency_additional.value; + // Field: hyperbus_cfg_regs.chip_5.burst_cfg.t_burst_max + always_comb begin + automatic logic [15:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.burst_cfg.t_burst_max.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.burst_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.burst_cfg.t_burst_max.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]); + load_next_c = '1; + end + field_combo.chip_5.burst_cfg.t_burst_max.next = next_c; + field_combo.chip_5.burst_cfg.t_burst_max.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.burst_cfg.t_burst_max.value <= 16'h15e; + end else begin + if(field_combo.chip_5.burst_cfg.t_burst_max.load_next) begin + field_storage.chip_5.burst_cfg.t_burst_max.value <= field_combo.chip_5.burst_cfg.t_burst_max.next; + end + end + end + assign hwif_out.chip_5.burst_cfg.t_burst_max.value = field_storage.chip_5.burst_cfg.t_burst_max.value; + // Field: hyperbus_cfg_regs.chip_5.chip_timing.t_read_write_recovery + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.chip_timing.t_read_write_recovery.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.chip_timing.t_read_write_recovery.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_5.chip_timing.t_read_write_recovery.next = next_c; + field_combo.chip_5.chip_timing.t_read_write_recovery.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.chip_timing.t_read_write_recovery.value <= 8'h6; + end else begin + if(field_combo.chip_5.chip_timing.t_read_write_recovery.load_next) begin + field_storage.chip_5.chip_timing.t_read_write_recovery.value <= field_combo.chip_5.chip_timing.t_read_write_recovery.next; + end + end + end + assign hwif_out.chip_5.chip_timing.t_read_write_recovery.value = field_storage.chip_5.chip_timing.t_read_write_recovery.value; + // Field: hyperbus_cfg_regs.chip_5.chip_timing.t_csh_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.chip_timing.t_csh_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.chip_timing.t_csh_cycles.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_5.chip_timing.t_csh_cycles.next = next_c; + field_combo.chip_5.chip_timing.t_csh_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.chip_timing.t_csh_cycles.value <= 8'h1; + end else begin + if(field_combo.chip_5.chip_timing.t_csh_cycles.load_next) begin + field_storage.chip_5.chip_timing.t_csh_cycles.value <= field_combo.chip_5.chip_timing.t_csh_cycles.next; + end + end + end + assign hwif_out.chip_5.chip_timing.t_csh_cycles.value = field_storage.chip_5.chip_timing.t_csh_cycles.value; + // Field: hyperbus_cfg_regs.chip_5.chip_timing.csn_to_ck_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.chip_timing.csn_to_ck_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.chip_timing.csn_to_ck_cycles.value & ~decoded_wr_biten[23:16]) | (decoded_wr_data[23:16] & decoded_wr_biten[23:16]); + load_next_c = '1; + end + field_combo.chip_5.chip_timing.csn_to_ck_cycles.next = next_c; + field_combo.chip_5.chip_timing.csn_to_ck_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.chip_timing.csn_to_ck_cycles.value <= 8'h0; + end else begin + if(field_combo.chip_5.chip_timing.csn_to_ck_cycles.load_next) begin + field_storage.chip_5.chip_timing.csn_to_ck_cycles.value <= field_combo.chip_5.chip_timing.csn_to_ck_cycles.next; + end + end + end + assign hwif_out.chip_5.chip_timing.csn_to_ck_cycles.value = field_storage.chip_5.chip_timing.csn_to_ck_cycles.value; + // Field: hyperbus_cfg_regs.chip_5.rx_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_5.rx_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_5.rx_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_5.rx_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.chip_5.rx_delay.value.next = next_c; + field_combo.chip_5.rx_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_5.rx_delay.value.value <= 8'h10; + end else begin + if(field_combo.chip_5.rx_delay.value.load_next) begin + field_storage.chip_5.rx_delay.value.value <= field_combo.chip_5.rx_delay.value.next; + end + end + end + assign hwif_out.chip_5.rx_delay.value.value = field_storage.chip_5.rx_delay.value.value; + // Field: hyperbus_cfg_regs.chip_6.range_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.range_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.range_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.range_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.chip_6.range_base.value.next = next_c; + field_combo.chip_6.range_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.range_base.value.value <= 10'h6; + end else begin + if(field_combo.chip_6.range_base.value.load_next) begin + field_storage.chip_6.range_base.value.value <= field_combo.chip_6.range_base.value.next; + end + end + end + assign hwif_out.chip_6.range_base.value.value = field_storage.chip_6.range_base.value.value; + // Field: hyperbus_cfg_regs.chip_6.range_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.range_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.range_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.range_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.chip_6.range_bound.value.next = next_c; + field_combo.chip_6.range_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.range_bound.value.value <= 10'h7; + end else begin + if(field_combo.chip_6.range_bound.value.load_next) begin + field_storage.chip_6.range_bound.value.value <= field_combo.chip_6.range_bound.value.next; + end + end + end + assign hwif_out.chip_6.range_bound.value.value = field_storage.chip_6.range_bound.value.value; + // Field: hyperbus_cfg_regs.chip_6.address_cfg.address_space + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.address_cfg.address_space.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.address_cfg.address_space.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_6.address_cfg.address_space.next = next_c; + field_combo.chip_6.address_cfg.address_space.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.address_cfg.address_space.value <= 8'h0; + end else begin + if(field_combo.chip_6.address_cfg.address_space.load_next) begin + field_storage.chip_6.address_cfg.address_space.value <= field_combo.chip_6.address_cfg.address_space.next; + end + end + end + assign hwif_out.chip_6.address_cfg.address_space.value = field_storage.chip_6.address_cfg.address_space.value; + // Field: hyperbus_cfg_regs.chip_6.address_cfg.address_mask_msb + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.address_cfg.address_mask_msb.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.address_cfg.address_mask_msb.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_6.address_cfg.address_mask_msb.next = next_c; + field_combo.chip_6.address_cfg.address_mask_msb.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.address_cfg.address_mask_msb.value <= 8'h19; + end else begin + if(field_combo.chip_6.address_cfg.address_mask_msb.load_next) begin + field_storage.chip_6.address_cfg.address_mask_msb.value <= field_combo.chip_6.address_cfg.address_mask_msb.next; + end + end + end + assign hwif_out.chip_6.address_cfg.address_mask_msb.value = field_storage.chip_6.address_cfg.address_mask_msb.value; + // Field: hyperbus_cfg_regs.chip_6.address_cfg.enable + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.address_cfg.enable.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.address_cfg.enable.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_6.address_cfg.enable.next = next_c; + field_combo.chip_6.address_cfg.enable.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.address_cfg.enable.value <= 1'h1; + end else begin + if(field_combo.chip_6.address_cfg.enable.load_next) begin + field_storage.chip_6.address_cfg.enable.value <= field_combo.chip_6.address_cfg.enable.next; + end + end + end + assign hwif_out.chip_6.address_cfg.enable.value = field_storage.chip_6.address_cfg.enable.value; + // Field: hyperbus_cfg_regs.chip_6.latency_cfg.t_latency_access + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.latency_cfg.t_latency_access.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.latency_cfg.t_latency_access.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_6.latency_cfg.t_latency_access.next = next_c; + field_combo.chip_6.latency_cfg.t_latency_access.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.latency_cfg.t_latency_access.value <= 8'h6; + end else begin + if(field_combo.chip_6.latency_cfg.t_latency_access.load_next) begin + field_storage.chip_6.latency_cfg.t_latency_access.value <= field_combo.chip_6.latency_cfg.t_latency_access.next; + end + end + end + assign hwif_out.chip_6.latency_cfg.t_latency_access.value = field_storage.chip_6.latency_cfg.t_latency_access.value; + // Field: hyperbus_cfg_regs.chip_6.latency_cfg.rwds_sample_delay + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.latency_cfg.rwds_sample_delay.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.latency_cfg.rwds_sample_delay.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_6.latency_cfg.rwds_sample_delay.next = next_c; + field_combo.chip_6.latency_cfg.rwds_sample_delay.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.latency_cfg.rwds_sample_delay.value <= 8'h0; + end else begin + if(field_combo.chip_6.latency_cfg.rwds_sample_delay.load_next) begin + field_storage.chip_6.latency_cfg.rwds_sample_delay.value <= field_combo.chip_6.latency_cfg.rwds_sample_delay.next; + end + end + end + assign hwif_out.chip_6.latency_cfg.rwds_sample_delay.value = field_storage.chip_6.latency_cfg.rwds_sample_delay.value; + // Field: hyperbus_cfg_regs.chip_6.latency_cfg.en_latency_additional + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.latency_cfg.en_latency_additional.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.latency_cfg.en_latency_additional.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_6.latency_cfg.en_latency_additional.next = next_c; + field_combo.chip_6.latency_cfg.en_latency_additional.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.latency_cfg.en_latency_additional.value <= 1'h0; + end else begin + if(field_combo.chip_6.latency_cfg.en_latency_additional.load_next) begin + field_storage.chip_6.latency_cfg.en_latency_additional.value <= field_combo.chip_6.latency_cfg.en_latency_additional.next; + end + end + end + assign hwif_out.chip_6.latency_cfg.en_latency_additional.value = field_storage.chip_6.latency_cfg.en_latency_additional.value; + // Field: hyperbus_cfg_regs.chip_6.burst_cfg.t_burst_max + always_comb begin + automatic logic [15:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.burst_cfg.t_burst_max.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.burst_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.burst_cfg.t_burst_max.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]); + load_next_c = '1; + end + field_combo.chip_6.burst_cfg.t_burst_max.next = next_c; + field_combo.chip_6.burst_cfg.t_burst_max.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.burst_cfg.t_burst_max.value <= 16'h15e; + end else begin + if(field_combo.chip_6.burst_cfg.t_burst_max.load_next) begin + field_storage.chip_6.burst_cfg.t_burst_max.value <= field_combo.chip_6.burst_cfg.t_burst_max.next; + end + end + end + assign hwif_out.chip_6.burst_cfg.t_burst_max.value = field_storage.chip_6.burst_cfg.t_burst_max.value; + // Field: hyperbus_cfg_regs.chip_6.chip_timing.t_read_write_recovery + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.chip_timing.t_read_write_recovery.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.chip_timing.t_read_write_recovery.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_6.chip_timing.t_read_write_recovery.next = next_c; + field_combo.chip_6.chip_timing.t_read_write_recovery.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.chip_timing.t_read_write_recovery.value <= 8'h6; + end else begin + if(field_combo.chip_6.chip_timing.t_read_write_recovery.load_next) begin + field_storage.chip_6.chip_timing.t_read_write_recovery.value <= field_combo.chip_6.chip_timing.t_read_write_recovery.next; + end + end + end + assign hwif_out.chip_6.chip_timing.t_read_write_recovery.value = field_storage.chip_6.chip_timing.t_read_write_recovery.value; + // Field: hyperbus_cfg_regs.chip_6.chip_timing.t_csh_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.chip_timing.t_csh_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.chip_timing.t_csh_cycles.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_6.chip_timing.t_csh_cycles.next = next_c; + field_combo.chip_6.chip_timing.t_csh_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.chip_timing.t_csh_cycles.value <= 8'h1; + end else begin + if(field_combo.chip_6.chip_timing.t_csh_cycles.load_next) begin + field_storage.chip_6.chip_timing.t_csh_cycles.value <= field_combo.chip_6.chip_timing.t_csh_cycles.next; + end + end + end + assign hwif_out.chip_6.chip_timing.t_csh_cycles.value = field_storage.chip_6.chip_timing.t_csh_cycles.value; + // Field: hyperbus_cfg_regs.chip_6.chip_timing.csn_to_ck_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.chip_timing.csn_to_ck_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.chip_timing.csn_to_ck_cycles.value & ~decoded_wr_biten[23:16]) | (decoded_wr_data[23:16] & decoded_wr_biten[23:16]); + load_next_c = '1; + end + field_combo.chip_6.chip_timing.csn_to_ck_cycles.next = next_c; + field_combo.chip_6.chip_timing.csn_to_ck_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.chip_timing.csn_to_ck_cycles.value <= 8'h0; + end else begin + if(field_combo.chip_6.chip_timing.csn_to_ck_cycles.load_next) begin + field_storage.chip_6.chip_timing.csn_to_ck_cycles.value <= field_combo.chip_6.chip_timing.csn_to_ck_cycles.next; + end + end + end + assign hwif_out.chip_6.chip_timing.csn_to_ck_cycles.value = field_storage.chip_6.chip_timing.csn_to_ck_cycles.value; + // Field: hyperbus_cfg_regs.chip_6.rx_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_6.rx_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_6.rx_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_6.rx_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.chip_6.rx_delay.value.next = next_c; + field_combo.chip_6.rx_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_6.rx_delay.value.value <= 8'h10; + end else begin + if(field_combo.chip_6.rx_delay.value.load_next) begin + field_storage.chip_6.rx_delay.value.value <= field_combo.chip_6.rx_delay.value.next; + end + end + end + assign hwif_out.chip_6.rx_delay.value.value = field_storage.chip_6.rx_delay.value.value; + // Field: hyperbus_cfg_regs.chip_7.range_base.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.range_base.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.range_base && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.range_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.chip_7.range_base.value.next = next_c; + field_combo.chip_7.range_base.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.range_base.value.value <= 10'h7; + end else begin + if(field_combo.chip_7.range_base.value.load_next) begin + field_storage.chip_7.range_base.value.value <= field_combo.chip_7.range_base.value.next; + end + end + end + assign hwif_out.chip_7.range_base.value.value = field_storage.chip_7.range_base.value.value; + // Field: hyperbus_cfg_regs.chip_7.range_bound.value + always_comb begin + automatic logic [9:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.range_bound.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.range_bound && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.range_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.chip_7.range_bound.value.next = next_c; + field_combo.chip_7.range_bound.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.range_bound.value.value <= 10'h8; + end else begin + if(field_combo.chip_7.range_bound.value.load_next) begin + field_storage.chip_7.range_bound.value.value <= field_combo.chip_7.range_bound.value.next; + end + end + end + assign hwif_out.chip_7.range_bound.value.value = field_storage.chip_7.range_bound.value.value; + // Field: hyperbus_cfg_regs.chip_7.address_cfg.address_space + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.address_cfg.address_space.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.address_cfg.address_space.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_7.address_cfg.address_space.next = next_c; + field_combo.chip_7.address_cfg.address_space.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.address_cfg.address_space.value <= 8'h0; + end else begin + if(field_combo.chip_7.address_cfg.address_space.load_next) begin + field_storage.chip_7.address_cfg.address_space.value <= field_combo.chip_7.address_cfg.address_space.next; + end + end + end + assign hwif_out.chip_7.address_cfg.address_space.value = field_storage.chip_7.address_cfg.address_space.value; + // Field: hyperbus_cfg_regs.chip_7.address_cfg.address_mask_msb + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.address_cfg.address_mask_msb.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.address_cfg.address_mask_msb.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_7.address_cfg.address_mask_msb.next = next_c; + field_combo.chip_7.address_cfg.address_mask_msb.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.address_cfg.address_mask_msb.value <= 8'h19; + end else begin + if(field_combo.chip_7.address_cfg.address_mask_msb.load_next) begin + field_storage.chip_7.address_cfg.address_mask_msb.value <= field_combo.chip_7.address_cfg.address_mask_msb.next; + end + end + end + assign hwif_out.chip_7.address_cfg.address_mask_msb.value = field_storage.chip_7.address_cfg.address_mask_msb.value; + // Field: hyperbus_cfg_regs.chip_7.address_cfg.enable + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.address_cfg.enable.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.address_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.address_cfg.enable.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_7.address_cfg.enable.next = next_c; + field_combo.chip_7.address_cfg.enable.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.address_cfg.enable.value <= 1'h1; + end else begin + if(field_combo.chip_7.address_cfg.enable.load_next) begin + field_storage.chip_7.address_cfg.enable.value <= field_combo.chip_7.address_cfg.enable.next; + end + end + end + assign hwif_out.chip_7.address_cfg.enable.value = field_storage.chip_7.address_cfg.enable.value; + // Field: hyperbus_cfg_regs.chip_7.latency_cfg.t_latency_access + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.latency_cfg.t_latency_access.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.latency_cfg.t_latency_access.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_7.latency_cfg.t_latency_access.next = next_c; + field_combo.chip_7.latency_cfg.t_latency_access.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.latency_cfg.t_latency_access.value <= 8'h6; + end else begin + if(field_combo.chip_7.latency_cfg.t_latency_access.load_next) begin + field_storage.chip_7.latency_cfg.t_latency_access.value <= field_combo.chip_7.latency_cfg.t_latency_access.next; + end + end + end + assign hwif_out.chip_7.latency_cfg.t_latency_access.value = field_storage.chip_7.latency_cfg.t_latency_access.value; + // Field: hyperbus_cfg_regs.chip_7.latency_cfg.rwds_sample_delay + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.latency_cfg.rwds_sample_delay.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.latency_cfg.rwds_sample_delay.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_7.latency_cfg.rwds_sample_delay.next = next_c; + field_combo.chip_7.latency_cfg.rwds_sample_delay.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.latency_cfg.rwds_sample_delay.value <= 8'h0; + end else begin + if(field_combo.chip_7.latency_cfg.rwds_sample_delay.load_next) begin + field_storage.chip_7.latency_cfg.rwds_sample_delay.value <= field_combo.chip_7.latency_cfg.rwds_sample_delay.next; + end + end + end + assign hwif_out.chip_7.latency_cfg.rwds_sample_delay.value = field_storage.chip_7.latency_cfg.rwds_sample_delay.value; + // Field: hyperbus_cfg_regs.chip_7.latency_cfg.en_latency_additional + always_comb begin + automatic logic [0:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.latency_cfg.en_latency_additional.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.latency_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.latency_cfg.en_latency_additional.value & ~decoded_wr_biten[16:16]) | (decoded_wr_data[16:16] & decoded_wr_biten[16:16]); + load_next_c = '1; + end + field_combo.chip_7.latency_cfg.en_latency_additional.next = next_c; + field_combo.chip_7.latency_cfg.en_latency_additional.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.latency_cfg.en_latency_additional.value <= 1'h0; + end else begin + if(field_combo.chip_7.latency_cfg.en_latency_additional.load_next) begin + field_storage.chip_7.latency_cfg.en_latency_additional.value <= field_combo.chip_7.latency_cfg.en_latency_additional.next; + end + end + end + assign hwif_out.chip_7.latency_cfg.en_latency_additional.value = field_storage.chip_7.latency_cfg.en_latency_additional.value; + // Field: hyperbus_cfg_regs.chip_7.burst_cfg.t_burst_max + always_comb begin + automatic logic [15:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.burst_cfg.t_burst_max.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.burst_cfg && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.burst_cfg.t_burst_max.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]); + load_next_c = '1; + end + field_combo.chip_7.burst_cfg.t_burst_max.next = next_c; + field_combo.chip_7.burst_cfg.t_burst_max.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.burst_cfg.t_burst_max.value <= 16'h15e; + end else begin + if(field_combo.chip_7.burst_cfg.t_burst_max.load_next) begin + field_storage.chip_7.burst_cfg.t_burst_max.value <= field_combo.chip_7.burst_cfg.t_burst_max.next; + end + end + end + assign hwif_out.chip_7.burst_cfg.t_burst_max.value = field_storage.chip_7.burst_cfg.t_burst_max.value; + // Field: hyperbus_cfg_regs.chip_7.chip_timing.t_read_write_recovery + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.chip_timing.t_read_write_recovery.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.chip_timing.t_read_write_recovery.value & ~decoded_wr_biten[7:0]) | (decoded_wr_data[7:0] & decoded_wr_biten[7:0]); + load_next_c = '1; + end + field_combo.chip_7.chip_timing.t_read_write_recovery.next = next_c; + field_combo.chip_7.chip_timing.t_read_write_recovery.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.chip_timing.t_read_write_recovery.value <= 8'h6; + end else begin + if(field_combo.chip_7.chip_timing.t_read_write_recovery.load_next) begin + field_storage.chip_7.chip_timing.t_read_write_recovery.value <= field_combo.chip_7.chip_timing.t_read_write_recovery.next; + end + end + end + assign hwif_out.chip_7.chip_timing.t_read_write_recovery.value = field_storage.chip_7.chip_timing.t_read_write_recovery.value; + // Field: hyperbus_cfg_regs.chip_7.chip_timing.t_csh_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.chip_timing.t_csh_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.chip_timing.t_csh_cycles.value & ~decoded_wr_biten[15:8]) | (decoded_wr_data[15:8] & decoded_wr_biten[15:8]); + load_next_c = '1; + end + field_combo.chip_7.chip_timing.t_csh_cycles.next = next_c; + field_combo.chip_7.chip_timing.t_csh_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.chip_timing.t_csh_cycles.value <= 8'h1; + end else begin + if(field_combo.chip_7.chip_timing.t_csh_cycles.load_next) begin + field_storage.chip_7.chip_timing.t_csh_cycles.value <= field_combo.chip_7.chip_timing.t_csh_cycles.next; + end + end + end + assign hwif_out.chip_7.chip_timing.t_csh_cycles.value = field_storage.chip_7.chip_timing.t_csh_cycles.value; + // Field: hyperbus_cfg_regs.chip_7.chip_timing.csn_to_ck_cycles + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.chip_timing.csn_to_ck_cycles.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.chip_timing && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.chip_timing.csn_to_ck_cycles.value & ~decoded_wr_biten[23:16]) | (decoded_wr_data[23:16] & decoded_wr_biten[23:16]); + load_next_c = '1; + end + field_combo.chip_7.chip_timing.csn_to_ck_cycles.next = next_c; + field_combo.chip_7.chip_timing.csn_to_ck_cycles.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.chip_timing.csn_to_ck_cycles.value <= 8'h0; + end else begin + if(field_combo.chip_7.chip_timing.csn_to_ck_cycles.load_next) begin + field_storage.chip_7.chip_timing.csn_to_ck_cycles.value <= field_combo.chip_7.chip_timing.csn_to_ck_cycles.next; + end + end + end + assign hwif_out.chip_7.chip_timing.csn_to_ck_cycles.value = field_storage.chip_7.chip_timing.csn_to_ck_cycles.value; + // Field: hyperbus_cfg_regs.chip_7.rx_delay.value + always_comb begin + automatic logic [7:0] next_c; + automatic logic load_next_c; + next_c = field_storage.chip_7.rx_delay.value.value; + load_next_c = '0; + if(decoded_reg_strb.chip_7.rx_delay && decoded_req_is_wr) begin // SW write + next_c = (field_storage.chip_7.rx_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.chip_7.rx_delay.value.next = next_c; + field_combo.chip_7.rx_delay.value.load_next = load_next_c; + end + always_ff @(posedge clk or negedge arst_n) begin + if(~arst_n) begin + field_storage.chip_7.rx_delay.value.value <= 8'h10; + end else begin + if(field_combo.chip_7.rx_delay.value.load_next) begin + field_storage.chip_7.rx_delay.value.value <= field_combo.chip_7.rx_delay.value.next; + end + end + end + assign hwif_out.chip_7.rx_delay.value.value = field_storage.chip_7.rx_delay.value.value; + assign hwif_out.reserved_top.value.value = 32'h0; + + //-------------------------------------------------------------------------- + // 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 [11: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 == 12'h0) begin + readback_data_var[7:0] = 8'h0; + readback_data_var[15:8] = 8'h9; + readback_data_var[23:16] = 8'h0; + readback_data_var[31:24] = 8'h0; + end + if(rd_mux_addr == 12'h4) begin + readback_data_var[15:0] = 16'h0; + readback_data_var[31:16] = 16'h1; + end + if(rd_mux_addr == 12'h8) begin + readback_data_var[7:0] = field_storage.global_cfg.capability.num_chips.value; + readback_data_var[15:8] = field_storage.global_cfg.capability.num_phys.value; + readback_data_var[16] = field_storage.global_cfg.capability.per_chip_cfg.value; + readback_data_var[17] = field_storage.global_cfg.capability.per_phy_cfg.value; + readback_data_var[18] = field_storage.global_cfg.capability.chip_enable.value; + readback_data_var[19] = field_storage.global_cfg.capability.clock_divider.value; + readback_data_var[20] = field_storage.global_cfg.capability.staged_apply.value; + readback_data_var[21] = field_storage.global_cfg.capability.error_status.value; + readback_data_var[22] = field_storage.global_cfg.capability.rwds_sample_timing.value; + readback_data_var[23] = field_storage.global_cfg.capability.rwds_oe_timing.value; + end + if(rd_mux_addr == 12'hc) begin + readback_data_var[0] = field_storage.global_cfg.command.flush.value; + readback_data_var[1] = field_storage.global_cfg.command.apply.value; + end + if(rd_mux_addr == 12'h10) begin + readback_data_var[0] = field_storage.global_cfg.status.decode_error.value; + readback_data_var[1] = field_storage.global_cfg.status.busy.value; + readback_data_var[2] = field_storage.global_cfg.status.dirty.value; + end + if(rd_mux_addr == 12'h100) begin + readback_data_var[0] = field_storage.frontend.frontend_cfg.dual_phy.value; + end + if(rd_mux_addr == 12'h200) begin + readback_data_var[7:0] = field_storage.backend.clock_cfg.divider.value; + end + if(rd_mux_addr == 12'h300) begin + readback_data_var[7:0] = field_storage.phy_0.tx_delay.value.value; + end + if(rd_mux_addr == 12'h304) begin + readback_data_var[7:0] = field_storage.phy_0.rwds_timing.rwds_oe_setup_cycles.value; + end + if(rd_mux_addr == 12'h340) begin + readback_data_var[7:0] = field_storage.phy_1.tx_delay.value.value; + end + if(rd_mux_addr == 12'h344) begin + readback_data_var[7:0] = field_storage.phy_1.rwds_timing.rwds_oe_setup_cycles.value; + end + if(rd_mux_addr == 12'h400) begin + readback_data_var[31:22] = field_storage.chip_0.range_base.value.value; + end + if(rd_mux_addr == 12'h404) begin + readback_data_var[31:22] = field_storage.chip_0.range_bound.value.value; + end + if(rd_mux_addr == 12'h408) begin + readback_data_var[7:0] = field_storage.chip_0.address_cfg.address_space.value; + readback_data_var[15:8] = field_storage.chip_0.address_cfg.address_mask_msb.value; + readback_data_var[16] = field_storage.chip_0.address_cfg.enable.value; + end + if(rd_mux_addr == 12'h40c) begin + readback_data_var[7:0] = field_storage.chip_0.latency_cfg.t_latency_access.value; + readback_data_var[15:8] = field_storage.chip_0.latency_cfg.rwds_sample_delay.value; + readback_data_var[16] = field_storage.chip_0.latency_cfg.en_latency_additional.value; + end + if(rd_mux_addr == 12'h410) begin + readback_data_var[15:0] = field_storage.chip_0.burst_cfg.t_burst_max.value; + end + if(rd_mux_addr == 12'h414) begin + readback_data_var[7:0] = field_storage.chip_0.chip_timing.t_read_write_recovery.value; + readback_data_var[15:8] = field_storage.chip_0.chip_timing.t_csh_cycles.value; + readback_data_var[23:16] = field_storage.chip_0.chip_timing.csn_to_ck_cycles.value; + end + if(rd_mux_addr == 12'h418) begin + readback_data_var[7:0] = field_storage.chip_0.rx_delay.value.value; + end + if(rd_mux_addr == 12'h440) begin + readback_data_var[31:22] = field_storage.chip_1.range_base.value.value; + end + if(rd_mux_addr == 12'h444) begin + readback_data_var[31:22] = field_storage.chip_1.range_bound.value.value; + end + if(rd_mux_addr == 12'h448) begin + readback_data_var[7:0] = field_storage.chip_1.address_cfg.address_space.value; + readback_data_var[15:8] = field_storage.chip_1.address_cfg.address_mask_msb.value; + readback_data_var[16] = field_storage.chip_1.address_cfg.enable.value; + end + if(rd_mux_addr == 12'h44c) begin + readback_data_var[7:0] = field_storage.chip_1.latency_cfg.t_latency_access.value; + readback_data_var[15:8] = field_storage.chip_1.latency_cfg.rwds_sample_delay.value; + readback_data_var[16] = field_storage.chip_1.latency_cfg.en_latency_additional.value; + end + if(rd_mux_addr == 12'h450) begin + readback_data_var[15:0] = field_storage.chip_1.burst_cfg.t_burst_max.value; + end + if(rd_mux_addr == 12'h454) begin + readback_data_var[7:0] = field_storage.chip_1.chip_timing.t_read_write_recovery.value; + readback_data_var[15:8] = field_storage.chip_1.chip_timing.t_csh_cycles.value; + readback_data_var[23:16] = field_storage.chip_1.chip_timing.csn_to_ck_cycles.value; + end + if(rd_mux_addr == 12'h458) begin + readback_data_var[7:0] = field_storage.chip_1.rx_delay.value.value; + end + if(rd_mux_addr == 12'h480) begin + readback_data_var[31:22] = field_storage.chip_2.range_base.value.value; + end + if(rd_mux_addr == 12'h484) begin + readback_data_var[31:22] = field_storage.chip_2.range_bound.value.value; + end + if(rd_mux_addr == 12'h488) begin + readback_data_var[7:0] = field_storage.chip_2.address_cfg.address_space.value; + readback_data_var[15:8] = field_storage.chip_2.address_cfg.address_mask_msb.value; + readback_data_var[16] = field_storage.chip_2.address_cfg.enable.value; + end + if(rd_mux_addr == 12'h48c) begin + readback_data_var[7:0] = field_storage.chip_2.latency_cfg.t_latency_access.value; + readback_data_var[15:8] = field_storage.chip_2.latency_cfg.rwds_sample_delay.value; + readback_data_var[16] = field_storage.chip_2.latency_cfg.en_latency_additional.value; + end + if(rd_mux_addr == 12'h490) begin + readback_data_var[15:0] = field_storage.chip_2.burst_cfg.t_burst_max.value; + end + if(rd_mux_addr == 12'h494) begin + readback_data_var[7:0] = field_storage.chip_2.chip_timing.t_read_write_recovery.value; + readback_data_var[15:8] = field_storage.chip_2.chip_timing.t_csh_cycles.value; + readback_data_var[23:16] = field_storage.chip_2.chip_timing.csn_to_ck_cycles.value; + end + if(rd_mux_addr == 12'h498) begin + readback_data_var[7:0] = field_storage.chip_2.rx_delay.value.value; + end + if(rd_mux_addr == 12'h4c0) begin + readback_data_var[31:22] = field_storage.chip_3.range_base.value.value; + end + if(rd_mux_addr == 12'h4c4) begin + readback_data_var[31:22] = field_storage.chip_3.range_bound.value.value; + end + if(rd_mux_addr == 12'h4c8) begin + readback_data_var[7:0] = field_storage.chip_3.address_cfg.address_space.value; + readback_data_var[15:8] = field_storage.chip_3.address_cfg.address_mask_msb.value; + readback_data_var[16] = field_storage.chip_3.address_cfg.enable.value; + end + if(rd_mux_addr == 12'h4cc) begin + readback_data_var[7:0] = field_storage.chip_3.latency_cfg.t_latency_access.value; + readback_data_var[15:8] = field_storage.chip_3.latency_cfg.rwds_sample_delay.value; + readback_data_var[16] = field_storage.chip_3.latency_cfg.en_latency_additional.value; + end + if(rd_mux_addr == 12'h4d0) begin + readback_data_var[15:0] = field_storage.chip_3.burst_cfg.t_burst_max.value; + end + if(rd_mux_addr == 12'h4d4) begin + readback_data_var[7:0] = field_storage.chip_3.chip_timing.t_read_write_recovery.value; + readback_data_var[15:8] = field_storage.chip_3.chip_timing.t_csh_cycles.value; + readback_data_var[23:16] = field_storage.chip_3.chip_timing.csn_to_ck_cycles.value; + end + if(rd_mux_addr == 12'h4d8) begin + readback_data_var[7:0] = field_storage.chip_3.rx_delay.value.value; + end + if(rd_mux_addr == 12'h500) begin + readback_data_var[31:22] = field_storage.chip_4.range_base.value.value; + end + if(rd_mux_addr == 12'h504) begin + readback_data_var[31:22] = field_storage.chip_4.range_bound.value.value; + end + if(rd_mux_addr == 12'h508) begin + readback_data_var[7:0] = field_storage.chip_4.address_cfg.address_space.value; + readback_data_var[15:8] = field_storage.chip_4.address_cfg.address_mask_msb.value; + readback_data_var[16] = field_storage.chip_4.address_cfg.enable.value; + end + if(rd_mux_addr == 12'h50c) begin + readback_data_var[7:0] = field_storage.chip_4.latency_cfg.t_latency_access.value; + readback_data_var[15:8] = field_storage.chip_4.latency_cfg.rwds_sample_delay.value; + readback_data_var[16] = field_storage.chip_4.latency_cfg.en_latency_additional.value; + end + if(rd_mux_addr == 12'h510) begin + readback_data_var[15:0] = field_storage.chip_4.burst_cfg.t_burst_max.value; + end + if(rd_mux_addr == 12'h514) begin + readback_data_var[7:0] = field_storage.chip_4.chip_timing.t_read_write_recovery.value; + readback_data_var[15:8] = field_storage.chip_4.chip_timing.t_csh_cycles.value; + readback_data_var[23:16] = field_storage.chip_4.chip_timing.csn_to_ck_cycles.value; + end + if(rd_mux_addr == 12'h518) begin + readback_data_var[7:0] = field_storage.chip_4.rx_delay.value.value; + end + if(rd_mux_addr == 12'h540) begin + readback_data_var[31:22] = field_storage.chip_5.range_base.value.value; + end + if(rd_mux_addr == 12'h544) begin + readback_data_var[31:22] = field_storage.chip_5.range_bound.value.value; + end + if(rd_mux_addr == 12'h548) begin + readback_data_var[7:0] = field_storage.chip_5.address_cfg.address_space.value; + readback_data_var[15:8] = field_storage.chip_5.address_cfg.address_mask_msb.value; + readback_data_var[16] = field_storage.chip_5.address_cfg.enable.value; + end + if(rd_mux_addr == 12'h54c) begin + readback_data_var[7:0] = field_storage.chip_5.latency_cfg.t_latency_access.value; + readback_data_var[15:8] = field_storage.chip_5.latency_cfg.rwds_sample_delay.value; + readback_data_var[16] = field_storage.chip_5.latency_cfg.en_latency_additional.value; + end + if(rd_mux_addr == 12'h550) begin + readback_data_var[15:0] = field_storage.chip_5.burst_cfg.t_burst_max.value; + end + if(rd_mux_addr == 12'h554) begin + readback_data_var[7:0] = field_storage.chip_5.chip_timing.t_read_write_recovery.value; + readback_data_var[15:8] = field_storage.chip_5.chip_timing.t_csh_cycles.value; + readback_data_var[23:16] = field_storage.chip_5.chip_timing.csn_to_ck_cycles.value; + end + if(rd_mux_addr == 12'h558) begin + readback_data_var[7:0] = field_storage.chip_5.rx_delay.value.value; + end + if(rd_mux_addr == 12'h580) begin + readback_data_var[31:22] = field_storage.chip_6.range_base.value.value; + end + if(rd_mux_addr == 12'h584) begin + readback_data_var[31:22] = field_storage.chip_6.range_bound.value.value; + end + if(rd_mux_addr == 12'h588) begin + readback_data_var[7:0] = field_storage.chip_6.address_cfg.address_space.value; + readback_data_var[15:8] = field_storage.chip_6.address_cfg.address_mask_msb.value; + readback_data_var[16] = field_storage.chip_6.address_cfg.enable.value; + end + if(rd_mux_addr == 12'h58c) begin + readback_data_var[7:0] = field_storage.chip_6.latency_cfg.t_latency_access.value; + readback_data_var[15:8] = field_storage.chip_6.latency_cfg.rwds_sample_delay.value; + readback_data_var[16] = field_storage.chip_6.latency_cfg.en_latency_additional.value; + end + if(rd_mux_addr == 12'h590) begin + readback_data_var[15:0] = field_storage.chip_6.burst_cfg.t_burst_max.value; + end + if(rd_mux_addr == 12'h594) begin + readback_data_var[7:0] = field_storage.chip_6.chip_timing.t_read_write_recovery.value; + readback_data_var[15:8] = field_storage.chip_6.chip_timing.t_csh_cycles.value; + readback_data_var[23:16] = field_storage.chip_6.chip_timing.csn_to_ck_cycles.value; + end + if(rd_mux_addr == 12'h598) begin + readback_data_var[7:0] = field_storage.chip_6.rx_delay.value.value; + end + if(rd_mux_addr == 12'h5c0) begin + readback_data_var[31:22] = field_storage.chip_7.range_base.value.value; + end + if(rd_mux_addr == 12'h5c4) begin + readback_data_var[31:22] = field_storage.chip_7.range_bound.value.value; + end + if(rd_mux_addr == 12'h5c8) begin + readback_data_var[7:0] = field_storage.chip_7.address_cfg.address_space.value; + readback_data_var[15:8] = field_storage.chip_7.address_cfg.address_mask_msb.value; + readback_data_var[16] = field_storage.chip_7.address_cfg.enable.value; + end + if(rd_mux_addr == 12'h5cc) begin + readback_data_var[7:0] = field_storage.chip_7.latency_cfg.t_latency_access.value; + readback_data_var[15:8] = field_storage.chip_7.latency_cfg.rwds_sample_delay.value; + readback_data_var[16] = field_storage.chip_7.latency_cfg.en_latency_additional.value; + end + if(rd_mux_addr == 12'h5d0) begin + readback_data_var[15:0] = field_storage.chip_7.burst_cfg.t_burst_max.value; + end + if(rd_mux_addr == 12'h5d4) begin + readback_data_var[7:0] = field_storage.chip_7.chip_timing.t_read_write_recovery.value; + readback_data_var[15:8] = field_storage.chip_7.chip_timing.t_csh_cycles.value; + readback_data_var[23:16] = field_storage.chip_7.chip_timing.csn_to_ck_cycles.value; + end + if(rd_mux_addr == 12'h5d8) begin + readback_data_var[7:0] = field_storage.chip_7.rx_delay.value.value; + end + if(rd_mux_addr == 12'hffc) begin + readback_data_var[31:0] = 32'h0; + 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..807817d --- /dev/null +++ b/src/regs/hyperbus_cfg_regblock_pkg.sv @@ -0,0 +1,566 @@ +// 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 = 12; + localparam HYPERBUS_CFG_REGBLOCK_SIZE = 'h1000; + + typedef struct { + logic [7:0] next; + } hyperbus_cfg_regs__capability__num_chips__in_t; + + typedef struct { + logic [7:0] next; + } hyperbus_cfg_regs__capability__num_phys__in_t; + + typedef struct { + logic next; + } hyperbus_cfg_regs__capability__per_chip_cfg__in_t; + + typedef struct { + logic next; + } hyperbus_cfg_regs__capability__per_phy_cfg__in_t; + + typedef struct { + logic next; + } hyperbus_cfg_regs__capability__chip_enable__in_t; + + typedef struct { + logic next; + } hyperbus_cfg_regs__capability__clock_divider__in_t; + + typedef struct { + logic next; + } hyperbus_cfg_regs__capability__staged_apply__in_t; + + typedef struct { + logic next; + } hyperbus_cfg_regs__capability__error_status__in_t; + + typedef struct { + logic next; + } hyperbus_cfg_regs__capability__rwds_sample_timing__in_t; + + typedef struct { + logic next; + } hyperbus_cfg_regs__capability__rwds_oe_timing__in_t; + + typedef struct { + hyperbus_cfg_regs__capability__num_chips__in_t num_chips; + hyperbus_cfg_regs__capability__num_phys__in_t num_phys; + hyperbus_cfg_regs__capability__per_chip_cfg__in_t per_chip_cfg; + hyperbus_cfg_regs__capability__per_phy_cfg__in_t per_phy_cfg; + hyperbus_cfg_regs__capability__chip_enable__in_t chip_enable; + hyperbus_cfg_regs__capability__clock_divider__in_t clock_divider; + hyperbus_cfg_regs__capability__staged_apply__in_t staged_apply; + hyperbus_cfg_regs__capability__error_status__in_t error_status; + hyperbus_cfg_regs__capability__rwds_sample_timing__in_t rwds_sample_timing; + hyperbus_cfg_regs__capability__rwds_oe_timing__in_t rwds_oe_timing; + } hyperbus_cfg_regs__capability__in_t; + + typedef struct { + logic next; + logic hwset; + } hyperbus_cfg_regs__status__decode_error__in_t; + + typedef struct { + logic next; + } hyperbus_cfg_regs__status__busy__in_t; + + typedef struct { + logic next; + } hyperbus_cfg_regs__status__dirty__in_t; + + typedef struct { + hyperbus_cfg_regs__status__decode_error__in_t decode_error; + hyperbus_cfg_regs__status__busy__in_t busy; + hyperbus_cfg_regs__status__dirty__in_t dirty; + } hyperbus_cfg_regs__status__in_t; + + typedef struct { + hyperbus_cfg_regs__capability__in_t capability; + hyperbus_cfg_regs__status__in_t status; + } hyperbus_cfg_regs__global_regs__in_t; + + typedef struct { + hyperbus_cfg_regs__global_regs__in_t global_cfg; + } hyperbus_cfg_regs__in_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__ip_version__revision__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__ip_version__patch__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__ip_version__minor__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__ip_version__major__out_t; + + typedef struct { + hyperbus_cfg_regs__ip_version__revision__out_t revision; + hyperbus_cfg_regs__ip_version__patch__out_t patch; + hyperbus_cfg_regs__ip_version__minor__out_t minor; + hyperbus_cfg_regs__ip_version__major__out_t major; + } hyperbus_cfg_regs__ip_version__out_t; + + typedef struct { + logic [15:0] value; + } hyperbus_cfg_regs__reg_if_version__minor__out_t; + + typedef struct { + logic [15:0] value; + } hyperbus_cfg_regs__reg_if_version__major__out_t; + + typedef struct { + hyperbus_cfg_regs__reg_if_version__minor__out_t minor; + hyperbus_cfg_regs__reg_if_version__major__out_t major; + } hyperbus_cfg_regs__reg_if_version__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__capability__num_chips__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__capability__num_phys__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__capability__per_chip_cfg__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__capability__per_phy_cfg__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__capability__chip_enable__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__capability__clock_divider__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__capability__staged_apply__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__capability__error_status__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__capability__rwds_sample_timing__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__capability__rwds_oe_timing__out_t; + + typedef struct { + hyperbus_cfg_regs__capability__num_chips__out_t num_chips; + hyperbus_cfg_regs__capability__num_phys__out_t num_phys; + hyperbus_cfg_regs__capability__per_chip_cfg__out_t per_chip_cfg; + hyperbus_cfg_regs__capability__per_phy_cfg__out_t per_phy_cfg; + hyperbus_cfg_regs__capability__chip_enable__out_t chip_enable; + hyperbus_cfg_regs__capability__clock_divider__out_t clock_divider; + hyperbus_cfg_regs__capability__staged_apply__out_t staged_apply; + hyperbus_cfg_regs__capability__error_status__out_t error_status; + hyperbus_cfg_regs__capability__rwds_sample_timing__out_t rwds_sample_timing; + hyperbus_cfg_regs__capability__rwds_oe_timing__out_t rwds_oe_timing; + } hyperbus_cfg_regs__capability__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__command__flush__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__command__apply__out_t; + + typedef struct { + hyperbus_cfg_regs__command__flush__out_t flush; + hyperbus_cfg_regs__command__apply__out_t apply; + } hyperbus_cfg_regs__command__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__status__decode_error__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__status__busy__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__status__dirty__out_t; + + typedef struct { + hyperbus_cfg_regs__status__decode_error__out_t decode_error; + hyperbus_cfg_regs__status__busy__out_t busy; + hyperbus_cfg_regs__status__dirty__out_t dirty; + } hyperbus_cfg_regs__status__out_t; + + typedef struct { + hyperbus_cfg_regs__ip_version__out_t ip_version; + hyperbus_cfg_regs__reg_if_version__out_t reg_if_version; + hyperbus_cfg_regs__capability__out_t capability; + hyperbus_cfg_regs__command__out_t command; + hyperbus_cfg_regs__status__out_t status; + } hyperbus_cfg_regs__global_regs__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__frontend_cfg__dual_phy__out_t; + + typedef struct { + hyperbus_cfg_regs__frontend_cfg__dual_phy__out_t dual_phy; + } hyperbus_cfg_regs__frontend_cfg__out_t; + + typedef struct { + hyperbus_cfg_regs__frontend_cfg__out_t frontend_cfg; + } hyperbus_cfg_regs__frontend_regs__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__clock_cfg__divider__out_t; + + typedef struct { + hyperbus_cfg_regs__clock_cfg__divider__out_t divider; + } hyperbus_cfg_regs__clock_cfg__out_t; + + typedef struct { + hyperbus_cfg_regs__clock_cfg__out_t clock_cfg; + } hyperbus_cfg_regs__backend_regs__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__tx_delay__value__out_t; + + typedef struct { + hyperbus_cfg_regs__tx_delay__value__out_t value; + } hyperbus_cfg_regs__tx_delay__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__rwds_timing__rwds_oe_setup_cycles__out_t; + + typedef struct { + hyperbus_cfg_regs__rwds_timing__rwds_oe_setup_cycles__out_t rwds_oe_setup_cycles; + } hyperbus_cfg_regs__rwds_timing__out_t; + + typedef struct { + hyperbus_cfg_regs__tx_delay__out_t tx_delay; + hyperbus_cfg_regs__rwds_timing__out_t rwds_timing; + } hyperbus_cfg_regs__phy_regs__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_base__value__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base__value__out_t value; + } hyperbus_cfg_regs__range_base__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_bound__value__out_t; + + typedef struct { + hyperbus_cfg_regs__range_bound__value__out_t value; + } hyperbus_cfg_regs__range_bound__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__address_cfg__address_space__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__address_cfg__address_mask_msb__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__address_cfg__enable__out_t; + + typedef struct { + hyperbus_cfg_regs__address_cfg__address_space__out_t address_space; + hyperbus_cfg_regs__address_cfg__address_mask_msb__out_t address_mask_msb; + hyperbus_cfg_regs__address_cfg__enable__out_t enable; + } hyperbus_cfg_regs__address_cfg__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__latency_cfg__t_latency_access__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__latency_cfg__rwds_sample_delay__out_t; + + typedef struct { + logic value; + } hyperbus_cfg_regs__latency_cfg__en_latency_additional__out_t; + + typedef struct { + hyperbus_cfg_regs__latency_cfg__t_latency_access__out_t t_latency_access; + hyperbus_cfg_regs__latency_cfg__rwds_sample_delay__out_t rwds_sample_delay; + hyperbus_cfg_regs__latency_cfg__en_latency_additional__out_t en_latency_additional; + } hyperbus_cfg_regs__latency_cfg__out_t; + + typedef struct { + logic [15:0] value; + } hyperbus_cfg_regs__burst_cfg__t_burst_max__out_t; + + typedef struct { + hyperbus_cfg_regs__burst_cfg__t_burst_max__out_t t_burst_max; + } hyperbus_cfg_regs__burst_cfg__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__chip_timing__t_read_write_recovery__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__chip_timing__t_csh_cycles__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__chip_timing__csn_to_ck_cycles__out_t; + + typedef struct { + hyperbus_cfg_regs__chip_timing__t_read_write_recovery__out_t t_read_write_recovery; + hyperbus_cfg_regs__chip_timing__t_csh_cycles__out_t t_csh_cycles; + hyperbus_cfg_regs__chip_timing__csn_to_ck_cycles__out_t csn_to_ck_cycles; + } hyperbus_cfg_regs__chip_timing__out_t; + + typedef struct { + logic [7:0] value; + } hyperbus_cfg_regs__rx_delay__value__out_t; + + typedef struct { + hyperbus_cfg_regs__rx_delay__value__out_t value; + } hyperbus_cfg_regs__rx_delay__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base__out_t range_base; + hyperbus_cfg_regs__range_bound__out_t range_bound; + hyperbus_cfg_regs__address_cfg__out_t address_cfg; + hyperbus_cfg_regs__latency_cfg__out_t latency_cfg; + hyperbus_cfg_regs__burst_cfg__out_t burst_cfg; + hyperbus_cfg_regs__chip_timing__out_t chip_timing; + hyperbus_cfg_regs__rx_delay__out_t rx_delay; + } hyperbus_cfg_regs__chip_regs__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_base_value_aea1d0e1__value_reset_1__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_aea1d0e1__value_reset_1__out_t value; + } hyperbus_cfg_regs__range_base_value_aea1d0e1__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_bound_value_e30f0c3f__value_reset_2__out_t; + + typedef struct { + hyperbus_cfg_regs__range_bound_value_e30f0c3f__value_reset_2__out_t value; + } hyperbus_cfg_regs__range_bound_value_e30f0c3f__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_aea1d0e1__out_t range_base; + hyperbus_cfg_regs__range_bound_value_e30f0c3f__out_t range_bound; + hyperbus_cfg_regs__address_cfg__out_t address_cfg; + hyperbus_cfg_regs__latency_cfg__out_t latency_cfg; + hyperbus_cfg_regs__burst_cfg__out_t burst_cfg; + hyperbus_cfg_regs__chip_timing__out_t chip_timing; + hyperbus_cfg_regs__rx_delay__out_t rx_delay; + } hyperbus_cfg_regs__chip_regs_range_base_de2a4636_range_bound_d40fd139__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_base_value_e30f0c3f__value_reset_2__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_e30f0c3f__value_reset_2__out_t value; + } hyperbus_cfg_regs__range_base_value_e30f0c3f__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_bound_value_2f629ec9__value_reset_3__out_t; + + typedef struct { + hyperbus_cfg_regs__range_bound_value_2f629ec9__value_reset_3__out_t value; + } hyperbus_cfg_regs__range_bound_value_2f629ec9__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_e30f0c3f__out_t range_base; + hyperbus_cfg_regs__range_bound_value_2f629ec9__out_t range_bound; + hyperbus_cfg_regs__address_cfg__out_t address_cfg; + hyperbus_cfg_regs__latency_cfg__out_t latency_cfg; + hyperbus_cfg_regs__burst_cfg__out_t burst_cfg; + hyperbus_cfg_regs__chip_timing__out_t chip_timing; + hyperbus_cfg_regs__rx_delay__out_t rx_delay; + } hyperbus_cfg_regs__chip_regs_range_base_b054dd4c_range_bound_a4d5f83b__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_base_value_2f629ec9__value_reset_3__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_2f629ec9__value_reset_3__out_t value; + } hyperbus_cfg_regs__range_base_value_2f629ec9__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_bound_value_a1e733c2__value_reset_4__out_t; + + typedef struct { + hyperbus_cfg_regs__range_bound_value_a1e733c2__value_reset_4__out_t value; + } hyperbus_cfg_regs__range_bound_value_a1e733c2__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_2f629ec9__out_t range_base; + hyperbus_cfg_regs__range_bound_value_a1e733c2__out_t range_bound; + hyperbus_cfg_regs__address_cfg__out_t address_cfg; + hyperbus_cfg_regs__latency_cfg__out_t latency_cfg; + hyperbus_cfg_regs__burst_cfg__out_t burst_cfg; + hyperbus_cfg_regs__chip_timing__out_t chip_timing; + hyperbus_cfg_regs__rx_delay__out_t rx_delay; + } hyperbus_cfg_regs__chip_regs_range_base_f7266ded_range_bound_5bf260e9__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_base_value_a1e733c2__value_reset_4__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_a1e733c2__value_reset_4__out_t value; + } hyperbus_cfg_regs__range_base_value_a1e733c2__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_bound_value_342915db__value_reset_5__out_t; + + typedef struct { + hyperbus_cfg_regs__range_bound_value_342915db__value_reset_5__out_t value; + } hyperbus_cfg_regs__range_bound_value_342915db__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_a1e733c2__out_t range_base; + hyperbus_cfg_regs__range_bound_value_342915db__out_t range_bound; + hyperbus_cfg_regs__address_cfg__out_t address_cfg; + hyperbus_cfg_regs__latency_cfg__out_t latency_cfg; + hyperbus_cfg_regs__burst_cfg__out_t burst_cfg; + hyperbus_cfg_regs__chip_timing__out_t chip_timing; + hyperbus_cfg_regs__rx_delay__out_t rx_delay; + } hyperbus_cfg_regs__chip_regs_range_base_0d543b99_range_bound_1fffc3a2__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_base_value_342915db__value_reset_5__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_342915db__value_reset_5__out_t value; + } hyperbus_cfg_regs__range_base_value_342915db__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_bound_value_883c384f__value_reset_6__out_t; + + typedef struct { + hyperbus_cfg_regs__range_bound_value_883c384f__value_reset_6__out_t value; + } hyperbus_cfg_regs__range_bound_value_883c384f__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_342915db__out_t range_base; + hyperbus_cfg_regs__range_bound_value_883c384f__out_t range_bound; + hyperbus_cfg_regs__address_cfg__out_t address_cfg; + hyperbus_cfg_regs__latency_cfg__out_t latency_cfg; + hyperbus_cfg_regs__burst_cfg__out_t burst_cfg; + hyperbus_cfg_regs__chip_timing__out_t chip_timing; + hyperbus_cfg_regs__rx_delay__out_t rx_delay; + } hyperbus_cfg_regs__chip_regs_range_base_e068ab2e_range_bound_9afa5911__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_base_value_883c384f__value_reset_6__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_883c384f__value_reset_6__out_t value; + } hyperbus_cfg_regs__range_base_value_883c384f__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_bound_value_d0d03147__value_reset_7__out_t; + + typedef struct { + hyperbus_cfg_regs__range_bound_value_d0d03147__value_reset_7__out_t value; + } hyperbus_cfg_regs__range_bound_value_d0d03147__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_883c384f__out_t range_base; + hyperbus_cfg_regs__range_bound_value_d0d03147__out_t range_bound; + hyperbus_cfg_regs__address_cfg__out_t address_cfg; + hyperbus_cfg_regs__latency_cfg__out_t latency_cfg; + hyperbus_cfg_regs__burst_cfg__out_t burst_cfg; + hyperbus_cfg_regs__chip_timing__out_t chip_timing; + hyperbus_cfg_regs__rx_delay__out_t rx_delay; + } hyperbus_cfg_regs__chip_regs_range_base_ac4fb754_range_bound_24b9644b__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_base_value_d0d03147__value_reset_7__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_d0d03147__value_reset_7__out_t value; + } hyperbus_cfg_regs__range_base_value_d0d03147__out_t; + + typedef struct { + logic [9:0] value; + } hyperbus_cfg_regs__range_bound_value_971bab53__value_reset_8__out_t; + + typedef struct { + hyperbus_cfg_regs__range_bound_value_971bab53__value_reset_8__out_t value; + } hyperbus_cfg_regs__range_bound_value_971bab53__out_t; + + typedef struct { + hyperbus_cfg_regs__range_base_value_d0d03147__out_t range_base; + hyperbus_cfg_regs__range_bound_value_971bab53__out_t range_bound; + hyperbus_cfg_regs__address_cfg__out_t address_cfg; + hyperbus_cfg_regs__latency_cfg__out_t latency_cfg; + hyperbus_cfg_regs__burst_cfg__out_t burst_cfg; + hyperbus_cfg_regs__chip_timing__out_t chip_timing; + hyperbus_cfg_regs__rx_delay__out_t rx_delay; + } hyperbus_cfg_regs__chip_regs_range_base_63c2ba90_range_bound_602aea67__out_t; + + typedef struct { + logic [31:0] value; + } hyperbus_cfg_regs__reserved_top__value__out_t; + + typedef struct { + hyperbus_cfg_regs__reserved_top__value__out_t value; + } hyperbus_cfg_regs__reserved_top__out_t; + + typedef struct { + hyperbus_cfg_regs__global_regs__out_t global_cfg; + hyperbus_cfg_regs__frontend_regs__out_t frontend; + hyperbus_cfg_regs__backend_regs__out_t backend; + hyperbus_cfg_regs__phy_regs__out_t phy_0; + hyperbus_cfg_regs__phy_regs__out_t phy_1; + hyperbus_cfg_regs__chip_regs__out_t chip_0; + hyperbus_cfg_regs__chip_regs_range_base_de2a4636_range_bound_d40fd139__out_t chip_1; + hyperbus_cfg_regs__chip_regs_range_base_b054dd4c_range_bound_a4d5f83b__out_t chip_2; + hyperbus_cfg_regs__chip_regs_range_base_f7266ded_range_bound_5bf260e9__out_t chip_3; + hyperbus_cfg_regs__chip_regs_range_base_0d543b99_range_bound_1fffc3a2__out_t chip_4; + hyperbus_cfg_regs__chip_regs_range_base_e068ab2e_range_bound_9afa5911__out_t chip_5; + hyperbus_cfg_regs__chip_regs_range_base_ac4fb754_range_bound_24b9644b__out_t chip_6; + hyperbus_cfg_regs__chip_regs_range_base_63c2ba90_range_bound_602aea67__out_t chip_7; + hyperbus_cfg_regs__reserved_top__out_t reserved_top; + } 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..a81f7f9 --- /dev/null +++ b/src/regs/hyperbus_cfg_regs.rdl @@ -0,0 +1,148 @@ +addrmap hyperbus_cfg_regs { + desc = "HyperBus controller configuration register map. Enabled chip ranges must be 4 MiB aligned, ordered by chip-select index, and non-overlapping."; + + reg ip_version { + desc = "IP version (major.minor.patch.revision)."; + field { sw = r; hw = r; reset = 8'd0; } major[31:24]; + field { sw = r; hw = r; reset = 8'd0; } minor[23:16]; + field { sw = r; hw = r; reset = 8'd9; } patch[15:8]; + field { sw = r; hw = r; reset = 8'd0; } revision[7:0]; + }; + reg reg_if_version { + desc = "Register interface version."; + field { sw = r; hw = r; reset = 16'd1; } major[31:16]; + field { sw = r; hw = r; reset = 16'd0; } minor[15:0]; + }; + reg capability { + desc = "Hardware-provided implementation capabilities."; + field { desc = "Number of implemented chip selects."; sw = r; hw = rw; reset = 8'd0; } num_chips[7:0]; + field { desc = "Number of implemented PHY lanes."; sw = r; hw = rw; reset = 8'd0; } num_phys[15:8]; + field { desc = "Each chip record controls its corresponding chip rather than chip 0 being shared."; sw = r; hw = rw; reset = 1'b0; } per_chip_cfg[16:16]; + field { desc = "Each PHY record controls its corresponding lane rather than PHY 0 being shared."; sw = r; hw = rw; reset = 1'b0; } per_phy_cfg[17:17]; + field { desc = "Per-chip enable bits participate in address decoding."; sw = r; hw = rw; reset = 1'b0; } chip_enable[18:18]; + field { desc = "CLOCK_CFG controls an implemented backend clock divider."; sw = r; hw = rw; reset = 1'b0; } clock_divider[19:19]; + field { desc = "FLUSH and APPLY commands implement a staged configuration barrier."; sw = r; hw = rw; reset = 1'b0; } staged_apply[20:20]; + field { desc = "STATUS reports sticky controller errors."; sw = r; hw = rw; reset = 1'b0; } error_status[21:21]; + field { desc = "Per-chip RWDS sample timing is implemented."; sw = r; hw = rw; reset = 1'b0; } rwds_sample_timing[22:22]; + field { desc = "Per-PHY RWDS output-enable timing is implemented."; sw = r; hw = rw; reset = 1'b0; } rwds_oe_timing[23:23]; + }; + reg command { + desc = "Single-cycle write-one command strobes, active when capability.staged_apply is set; otherwise writes are retained only as inert pulses."; + field { sw = rw; hw = r; singlepulse = true; reset = 1'b0; } flush; + field { sw = rw; hw = r; singlepulse = true; reset = 1'b0; } apply; + }; + reg status { + desc = "Controller status."; + field { sw = rw; hw = rw; onwrite = woclr; hwset = true; precedence = hw; reset = 1'b0; } decode_error; + field { sw = r; hw = rw; reset = 1'b0; } busy; + field { sw = r; hw = rw; reset = 1'b0; } dirty; + }; + reg frontend_cfg { + desc = "Frontend configuration. dual_phy must be 0 or 1."; + field { sw = rw; hw = r; reset = 1'b1; } dual_phy; + }; + reg clock_cfg { + desc = "Backend clock-divider configuration, active when capability.clock_divider is set; divider values must be at least 2."; + field { sw = rw; hw = r; reset = 8'd8; } divider[7:0]; + }; + reg tx_delay { + desc = "PHY transmit delay. PHY 0 is shared when capability.per_phy_cfg is clear."; + field { sw = rw; hw = r; reset = 8'h10; } value[7:0]; + }; + reg rwds_timing { + desc = "PHY RWDS output-enable timing. rwds_oe_setup_cycles must be at most 15 and is active when capability.rwds_oe_timing is set."; + field { sw = rw; hw = r; reset = 8'd1; } rwds_oe_setup_cycles[7:0]; + }; + reg range_base { + desc = "Inclusive 4 MiB-aligned chip address range base."; + field { sw = rw; hw = r; reset = 10'd0; } value[31:22]; + }; + reg range_bound { + desc = "Exclusive 4 MiB-aligned chip address range bound."; + field { sw = rw; hw = r; reset = 10'd1; } value[31:22]; + }; + reg address_cfg { + desc = "Chip address-space interpretation and enable. address_space and enable must be 0 or 1, and address_mask_msb must be at most 31. Chip 0 is shared when capability.per_chip_cfg is clear; enable is active when capability.chip_enable is set."; + field { sw = rw; hw = r; reset = 8'd0; } address_space[7:0]; + field { sw = rw; hw = r; reset = 8'd25; } address_mask_msb[15:8]; + field { sw = rw; hw = r; reset = 1'b1; } enable[16:16]; + }; + reg latency_cfg { + desc = "Chip latency configuration. t_latency_access must be 3 through 15; rwds_sample_delay must be at most 15 and is active when capability.rwds_sample_timing is set. Chip 0 is shared when capability.per_chip_cfg is clear."; + field { sw = rw; hw = r; reset = 8'd6; } t_latency_access[7:0]; + field { sw = rw; hw = r; reset = 8'd0; } rwds_sample_delay[15:8]; + field { sw = rw; hw = r; reset = 1'b0; } en_latency_additional[16:16]; + }; + reg burst_cfg { + desc = "Chip burst configuration. Chip 0 is shared when capability.per_chip_cfg is clear."; + field { sw = rw; hw = r; reset = 16'd350; } t_burst_max[15:0]; + }; + reg chip_timing { + desc = "Chip-select and read/write timing; byte-backed timing values must be at most 15. Chip 0 is shared when capability.per_chip_cfg is clear."; + field { sw = rw; hw = r; reset = 8'd6; } t_read_write_recovery[7:0]; + field { sw = rw; hw = r; reset = 8'd1; } t_csh_cycles[15:8]; + field { sw = rw; hw = r; reset = 8'd0; } csn_to_ck_cycles[23:16]; + }; + reg rx_delay { + desc = "Chip receive delay. Chip 0 is shared when capability.per_chip_cfg is clear."; + field { sw = rw; hw = r; reset = 8'h10; } value[7:0]; + }; + reg reserved_top { + desc = "Reserved endpoint that fixes the register window size to 4 KiB."; + field { sw = r; hw = r; reset = 32'd0; } value[31:0]; + }; + regfile phy_regs { + tx_delay tx_delay @0x00; + rwds_timing rwds_timing @0x04; + }; + regfile chip_regs { + range_base range_base @0x00; + range_bound range_bound @0x04; + address_cfg address_cfg @0x08; + latency_cfg latency_cfg @0x0c; + burst_cfg burst_cfg @0x10; + chip_timing chip_timing @0x14; + rx_delay rx_delay @0x18; + }; + regfile global_regs { + ip_version ip_version @0x000; + reg_if_version reg_if_version @0x004; + capability capability @0x008; + command command @0x00c; + status status @0x010; + }; + regfile frontend_regs { + frontend_cfg frontend_cfg @0x000; + }; + regfile backend_regs { + clock_cfg clock_cfg @0x000; + }; + global_regs global_cfg @0x000; + frontend_regs frontend @0x100; + backend_regs backend @0x200; + phy_regs phy_0 @0x300; + phy_regs phy_1 @0x340; + chip_regs chip_0 @0x400; + chip_regs chip_1 @0x440; + chip_regs chip_2 @0x480; + chip_regs chip_3 @0x4c0; + chip_regs chip_4 @0x500; + chip_regs chip_5 @0x540; + chip_regs chip_6 @0x580; + chip_regs chip_7 @0x5c0; + chip_1.range_base.value->reset = 10'd1; + chip_1.range_bound.value->reset = 10'd2; + chip_2.range_base.value->reset = 10'd2; + chip_2.range_bound.value->reset = 10'd3; + chip_3.range_base.value->reset = 10'd3; + chip_3.range_bound.value->reset = 10'd4; + chip_4.range_base.value->reset = 10'd4; + chip_4.range_bound.value->reset = 10'd5; + chip_5.range_base.value->reset = 10'd5; + chip_5.range_bound.value->reset = 10'd6; + chip_6.range_base.value->reset = 10'd6; + chip_6.range_bound.value->reset = 10'd7; + chip_7.range_base.value->reset = 10'd7; + chip_7.range_bound.value->reset = 10'd8; + reserved_top reserved_top @0xffc; +}; diff --git a/test/axi_hyper_tb.sv b/test/axi_hyper_tb.sv index fcd42cd..263eb71 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, @@ -23,10 +23,28 @@ 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. + parameter int unsigned TbRxDelayLineTaps = 16, + /// 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, + /// 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, + /// 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 ); import hyperbus_tb_pkg::*; ///////////////////////////// @@ -54,12 +72,30 @@ module axi_hyper_tb typedef axi_pkg::xbar_rule_32_t rule_t; localparam int unsigned RegBusDW = 32; - localparam int unsigned RegBusAW = 8; + // The stable register map is a hierarchical 4 KiB window. + localparam int unsigned RegBusAW = 12; localparam int unsigned TbDramDataWidth = 8; localparam int unsigned TbDramLenWidth = 32'h80000; 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 /////////////////////// @@ -259,6 +295,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 +399,707 @@ 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, + 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 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); + 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 + ); + 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'h410, saved_t_burst_max, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + + reg_drv.send_write(32'h410, 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'h410, saved_t_burst_max, '1, reg_error); + 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 cfg_done_time; + + write_done_time = 0; + cfg_done_time = 0; + $display("==========================="); + $display("= Config drain/apply ="); + $display("==========================="); + + fork + begin + axi_write_slow(axi_drv, BarrierAddr, 16, 16); + write_done_time = $time; + end + begin + repeat (32) @(posedge clk); + // Any accepted configuration write triggers the automatic drain/apply + // sequence; COMMAND remains inert until staged apply is implemented. + reg_drv.send_write(32'h410, 32'd350, '1, reg_error); + cfg_done_time = $time; + if (reg_error != 1'b0) begin + $error("[CFG-DRAIN] Configuration write returned an error"); + end + end + join + + if ((write_done_time == 0) || (cfg_done_time < write_done_time)) begin + $error("[CFG-DRAIN] Configuration completed at %0t before AXI write completed at %0t", + cfg_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'h010, 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'h010, 32'h1, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + reg_drv.send_read(32'h010, 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 (NumConnectedChips < 2) begin + return; + end + + $display("==========================="); + $display("= Cross-chip burst ="); + $display("==========================="); + + reg_drv.send_write(32'h404, Boundary, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + reg_drv.send_write(32'h440, 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'h440, 32'h8100_0000, '1, reg_error); + if (reg_error != 1'b0) $error("unexpected error"); + reg_drv.send_write(32'h404, 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 (NumConnectedChips < 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'h444, '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'h444, 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, + 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; + 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("= 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 + reg_drv.send_read(32'h010, 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'h010, 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. + 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 + reg_drv.send_read(32'h010, 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'h010, 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 + + 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 (NumConnectedChips < 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'h444, '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'h444, 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 ); @@ -359,6 +1107,11 @@ 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; + 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; @@ -375,19 +1128,108 @@ module axi_hyper_tb @(posedge rst_n); mst_scoreboard.monitor(); - #600350ns; + // Map each chip to a distinct 16 MiB host-address window. + if (NumConnectedChips > 1) begin + reg_master.send_write(32'h444, 32'h8200_0000, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_write(32'h440, 32'h8100_0000, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + end + reg_master.send_write(32'h404, 32'h8100_0000, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_write(32'h400, 32'h8000_0000, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); - // switch memory address space to register space - reg_master.send_write(32'h7<<2, 1'b1, '1, s_reg_error); + reg_master.send_write(32'h418, TbRxDelayLineTaps, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_write(32'h300, TbTxDelayLineTaps, '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)); + if (TbDutVariant == 0) begin + reg_master.send_read(32'h200, reg_read, s_reg_error); + if ((s_reg_error != 1'b0) || (reg_read != 8)) $error("unexpected divider reset value"); + reg_master.send_write(32'h200, 8'd2, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + end - // 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"); + #600350ns; + + if (TbDutVariant == 0) begin + // The configuration barrier completes only after the divided clock resumes. + reg_master.send_write(32'h200, 8'd4, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_read(32'h200, 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'h200, 8'd2, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + reg_master.send_read(32'h200, 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 + + 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); + 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, 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'h410, 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'h410, 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'h200, 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'h200, 8'd2, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected divider restore error"); + reg_master.send_write(32'h410, 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 + + if (TbDutVariant == 0) begin + // switch memory address space to register space + reg_master.send_write(32'h408, 32'h1_1901, '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'h408, 32'h1_1900, '1, s_reg_error); + if (s_reg_error != 1'b0) $error("unexpected error"); + end check_consecutive_reads(axi_ctrl_mst); @@ -411,31 +1253,7 @@ module axi_hyper_tb $display("= Use only phy 0 ="); $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); + reg_master.send_write(32'h100,1'b0,'1,s_reg_error); if (s_reg_error != 1'b0) $error("unexpected error"); axi_rand_mst.reset(); @@ -471,17 +1289,63 @@ module axi_hyper_tb .RegAw ( RegBusAW ), .RegDw ( RegBusDW ), - .NumChips ( NumChips ), + .NumConnectedChips ( NumConnectedChips ), .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 ), + .TbTxDelayLineTaps ( 31 ) + ) 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 + +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/axi_pre_aw_drain_tb.sv b/test/axi_pre_aw_drain_tb.sv new file mode 100644 index 0000000..74f3f88 --- /dev/null +++ b/test/axi_pre_aw_drain_tb.sv @@ -0,0 +1,180 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +`timescale 1 ns / 1 ps + +// Directed regression for AXI W-before-AW ordering across a configuration +// drain. The fixture is connected directly to the DUT so the AXI mux used by +// the larger randomized test cannot impose an AW-before-W ordering. +module axi_pre_aw_drain_tb; + + localparam int unsigned HostWriteBufferBytes = 128; + localparam int unsigned AxiDataBytes = 8; + localparam int unsigned NumTransactions = HostWriteBufferBytes / AxiDataBytes; + localparam int unsigned PartialBurstBeats = 32; + localparam logic [31:0] BaseAddress = 32'h0000_6800; + localparam logic [31:0] PartialBaseAddress = 32'h0000_7000; + // A regular stable-map configuration write starts the automatic drain/apply + // barrier in point5. The staged COMMAND/STATUS interface is not enabled. + localparam logic [31:0] DrainConfigAddr = 32'h410; + localparam logic [31:0] DrainConfigValue = 32'd350; + + bit complete_config_done; + bit partial_w_fifo_full; + bit partial_config_busy; + bit partial_config_done; + bit partial_w_done; + + fixture_hyperbus #( + .NumConnectedChips ( 2 ), + .NumPhys ( 2 ), + .DutVariant ( 1 ), + .HostWriteBufferBytes( HostWriteBufferBytes ), + .SysClkPeriod ( 10ns ), + .AnnotateSdf ( 1'b1 ) + ) fix (); + + initial begin + fix.reset_end(); + + // Fill all 16 entries of the 128-byte host W FIFO without presenting AW. + for (int unsigned transaction = 0; transaction < NumTransactions; transaction++) begin + fix.w_beat.w_data = 64'hd15e_a5e5_0000_0000 ^ + (BaseAddress + transaction * AxiDataBytes); + fix.w_beat.w_strb = '1; + fix.w_beat.w_last = 1'b1; + fix.axi_master_drv.send_w(fix.w_beat); + end + + // Start the automatic configuration barrier, then verify it remains busy until the + // matching AW requests have made the buffered W beats actionable. + complete_config_done = 1'b0; + fork + begin : config_complete_w_bursts + logic config_error; + fix.i_rmaster.send_write(DrainConfigAddr, DrainConfigValue, '1, config_error); + if (config_error) $fatal(1, "drain configuration write failed"); + complete_config_done = 1'b1; + end + join_none + repeat (4) @(posedge fix.sys_clk); + if (complete_config_done) begin + $fatal(1, "drain configuration write completed before buffered W bursts drained"); + end + + // Pair each AW with its B response to avoid filling the serializer's + // bounded write-ID queue while the drain is still active. + for (int unsigned transaction = 0; transaction < NumTransactions; transaction++) begin + fix.aw_beat.ax_addr = BaseAddress + transaction * AxiDataBytes; + fix.aw_beat.ax_id = transaction + 1; + fix.aw_beat.ax_len = '0; + fix.aw_beat.ax_size = 3; + fix.aw_beat.ax_burst = axi_pkg::BURST_INCR; + fix.aw_beat.ax_atop = '0; + fix.axi_master_drv.send_aw(fix.aw_beat); + fix.axi_master_drv.recv_b(fix.b_beat); + if ((fix.b_beat.b_resp != axi_pkg::RESP_OKAY) || + (fix.b_beat.b_id != transaction + 1)) begin + $fatal(1, "write %0d returned id=%0d resp=%0d", transaction, + fix.b_beat.b_id, fix.b_beat.b_resp); + end + end + + wait (complete_config_done); + + for (int unsigned transaction = 0; transaction < NumTransactions; transaction++) begin + fix.ar_beat.ax_addr = BaseAddress + transaction * AxiDataBytes; + fix.ar_beat.ax_id = transaction + 1; + fix.ar_beat.ax_len = '0; + fix.ar_beat.ax_size = 3; + fix.ar_beat.ax_burst = axi_pkg::BURST_INCR; + fix.axi_master_drv.send_ar(fix.ar_beat); + fix.axi_master_drv.recv_r(fix.r_beat); + if ((fix.r_beat.r_resp != axi_pkg::RESP_OKAY) || !fix.r_beat.r_last || + (fix.r_beat.r_data != (64'hd15e_a5e5_0000_0000 ^ + (BaseAddress + transaction * AxiDataBytes)))) begin + $fatal(1, "read %0d returned data=0x%016x last=%0b resp=%0d", transaction, + fix.r_beat.r_data, fix.r_beat.r_last, fix.r_beat.r_resp); + end + end + + // Start a burst longer than the host W FIFO before presenting its AW. The + // sender must stop at the 16-beat FIFO capacity while the drain barrier is + // entered, then resume only after the matching AW is accepted. + partial_w_fifo_full = 1'b0; + partial_config_busy = 1'b0; + partial_config_done = 1'b0; + partial_w_done = 1'b0; + fork + begin : send_partial_w_burst + for (int unsigned beat = 0; beat < PartialBurstBeats; beat++) begin + fix.w_beat.w_data = 64'hd15e_a5e5_0000_0000 ^ + (PartialBaseAddress + beat * AxiDataBytes); + fix.w_beat.w_strb = '1; + fix.w_beat.w_last = (beat == PartialBurstBeats - 1); + fix.axi_master_drv.send_w(fix.w_beat); + if (beat + 1 == NumTransactions) partial_w_fifo_full = 1'b1; + end + partial_w_done = 1'b1; + end + begin : config_partial_w_burst + logic partial_error; + + wait (partial_w_fifo_full); + repeat (2) @(posedge fix.sys_clk); + if (fix.axi_dv.w_ready !== 1'b0) begin + $fatal(1, "partial W burst did not reach FIFO backpressure"); + end + + partial_config_busy = 1'b1; + fix.i_rmaster.send_write(DrainConfigAddr, DrainConfigValue, '1, partial_error); + if (partial_error) $fatal(1, "partial drain configuration write failed"); + partial_config_done = 1'b1; + end + join_none + + wait (partial_config_busy); + fix.aw_beat.ax_addr = PartialBaseAddress; + fix.aw_beat.ax_id = 6'd32; + fix.aw_beat.ax_len = PartialBurstBeats - 1; + fix.aw_beat.ax_size = 3; + fix.aw_beat.ax_burst = axi_pkg::BURST_INCR; + fix.aw_beat.ax_atop = '0; + fix.axi_master_drv.send_aw(fix.aw_beat); + fix.axi_master_drv.recv_b(fix.b_beat); + if ((fix.b_beat.b_resp != axi_pkg::RESP_OKAY) || (fix.b_beat.b_id != 6'd32)) begin + $fatal(1, "partial write returned id=%0d resp=%0d", fix.b_beat.b_id, + fix.b_beat.b_resp); + end + wait (partial_w_done); + wait (partial_config_done); + + for (int unsigned beat = 0; beat < PartialBurstBeats; beat++) begin + fix.ar_beat.ax_addr = PartialBaseAddress + beat * AxiDataBytes; + fix.ar_beat.ax_id = beat + 1; + fix.ar_beat.ax_len = '0; + fix.ar_beat.ax_size = 3; + fix.ar_beat.ax_burst = axi_pkg::BURST_INCR; + fix.axi_master_drv.send_ar(fix.ar_beat); + fix.axi_master_drv.recv_r(fix.r_beat); + if ((fix.r_beat.r_resp != axi_pkg::RESP_OKAY) || !fix.r_beat.r_last || + (fix.r_beat.r_data != (64'hd15e_a5e5_0000_0000 ^ + (PartialBaseAddress + beat * AxiDataBytes)))) begin + $fatal(1, "partial read %0d returned data=0x%016x last=%0b resp=%0d", beat, + fix.r_beat.r_data, fix.r_beat.r_last, fix.r_beat.r_resp); + end + end + + fix.eos = 1'b1; + #100ns; + $display("Pre-AW W drain regressions passed"); + $finish; + end + + initial begin + #2ms; + $fatal(1, "Pre-AW W drain regression timed out"); + end + +endmodule diff --git a/test/dut_if.sv b/test/dut_if.sv index 232b541..d7485e8 100644 --- a/test/dut_if.sv +++ b/test/dut_if.sv @@ -22,14 +22,18 @@ 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, + 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 @@ -70,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; @@ -80,8 +84,11 @@ 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][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; @@ -122,9 +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 #( - .NumChips ( NumChips ), + hyperbus_test_dut #( + .DutVariant ( DutVariant ), .NumPhys ( NumPhys ), .AxiAddrWidth ( AxiAddrWidth ), .AxiDataWidth ( AxiDataWidth ), @@ -132,22 +161,18 @@ 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 ), - .RegAddrWidth ( RegAw ), .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 ), @@ -170,7 +195,7 @@ module dut_if generate for (genvar i=0; i