Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ sources:
- src/backend/idma_axis_write.sv
- src/backend/idma_channel_coupler.sv
- src/backend/idma_dataflow_element.sv
- src/backend/idma_otf_transpose.sv
- src/backend/idma_otf_compute.sv
- src/backend/idma_error_handler.sv
- src/backend/idma_init_read.sv
- src/backend/idma_init_write.sv
Expand All @@ -54,7 +56,6 @@ sources:
- src/backend/idma_tilelink_read.sv
- src/backend/idma_tilelink_write.sv

# Generated content
- target: rtl
files:
- target/rtl/idma_generated.sv
Expand All @@ -67,6 +68,7 @@ sources:
- src/midend/idma_mp_split_midend.sv
- src/midend/idma_nd_midend.sv
- src/midend/idma_rt_midend.sv
- src/midend/idma_transpose_midend.sv

# RISC-V opcode package for ooc use of inst64
- target: all(rtl,snitch_cluster)
Expand Down Expand Up @@ -120,7 +122,9 @@ sources:
- test/future/idma_tb_per2axi.sv
- test/future/TLToAXI4.v
- test/midend/tb_idma_nd_midend.sv
- test/midend/tb_idma_nd_midend_b2b.sv
- test/midend/tb_idma_rt_midend.sv
- test/midend/tb_idma_transpose_midend.sv
# Level 2
- test/future/idma_obi2axi_bridge.sv
- test/future/idma_tilelink2axi_bridge.sv
Expand All @@ -129,6 +133,8 @@ sources:
- target: idma_test
files:
- target/rtl/tb_idma_generated.sv
- test/tb_idma_transpose_nd.sv
- test/tb_idma_transpose_b2b.sv

# Multi-head directed backend testbenches
- target: multihead
Expand Down
86 changes: 78 additions & 8 deletions idma.mk
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ VCS ?= vcs
VERILATOR ?= verilator
VLOGAN ?= vlogan
VSIM ?= vsim
VLOG ?= vlog
VLIB ?= vlib

# Provision a local uv venv when the generator deps are not already available.
IDMA_VENV_PY := $(IDMA_ROOT)/.venv/bin/python
Expand Down Expand Up @@ -51,6 +53,14 @@ IDMA_OCCAMY_IDS := \
rw_axi_rw_init_rw_obi
IDMA_ADD_IDS ?=
IDMA_BACKEND_IDS ?= $(IDMA_BASE_IDS) $(IDMA_OCCAMY_IDS) $(IDMA_ADD_IDS)
# Compute-hosting variants (single AXI write); empty default = stock has no compute
IDMA_VIDMA_IDS ?=
# Compute variants (strip the optional :op:fd suffix) must be built backends
_idma_vidma_unknown := $(filter-out $(IDMA_BACKEND_IDS),\
$(foreach c,$(IDMA_VIDMA_IDS),$(firstword $(subst :, ,$(c)))))
ifneq ($(_idma_vidma_unknown),)
$(error iDMA: IDMA_VIDMA_IDS variant(s) not in IDMA_BACKEND_IDS: $(_idma_vidma_unknown))
endif

# generated frontends
IDMA_BASE_FE_IDS := reg32_3d reg64_2d reg64_1d
Expand Down Expand Up @@ -120,17 +130,26 @@ IDMA_RTL_FILES := $(IDMA_RTL_DIR)/idma_transport_layer \
IDMA_VSIM_DIR := $(IDMA_ROOT)/target/sim/vsim

define idma_gen
$(PYTHON) $(IDMA_GEN) --entity $1 --tpl $2 --db $3 --ids $4 --fids $5 > $6
$(PYTHON) $(IDMA_GEN) --entity $1 --tpl $2 --db $3 --ids $4 --fids $5 $(if $7,--compute-ids $7) > $6
endef

$(IDMA_RTL_DIR)/idma_transport_layer_%.sv: $(IDMA_GEN) $(IDMA_GEN_SRC) $(IDMA_ROOT)/src/backend/tpl/idma_transport_layer.sv.tpl $(IDMA_DB_FILES)
$(call idma_gen,transport,$(IDMA_ROOT)/src/backend/tpl/idma_transport_layer.sv.tpl,$(IDMA_DB_FILES),$*,,$@)
# Force an RTL regen when IDMA_VIDMA_IDS changes; rewritten only on change
IDMA_VIDMA_STAMP := $(IDMA_RTL_DIR)/.vidma_ids
.PHONY: idma_vidma_stamp_check
idma_vidma_stamp_check:
@mkdir -p $(IDMA_RTL_DIR)
@printf '%s' '$(IDMA_VIDMA_IDS)' | cmp -s - $(IDMA_VIDMA_STAMP) 2>/dev/null || \
printf '%s' '$(IDMA_VIDMA_IDS)' > $(IDMA_VIDMA_STAMP)
$(IDMA_VIDMA_STAMP): idma_vidma_stamp_check ;

$(IDMA_RTL_DIR)/idma_transport_layer_%.sv: $(IDMA_GEN) $(IDMA_GEN_SRC) $(IDMA_ROOT)/src/backend/tpl/idma_transport_layer.sv.tpl $(IDMA_DB_FILES) $(IDMA_VIDMA_STAMP)
$(call idma_gen,transport,$(IDMA_ROOT)/src/backend/tpl/idma_transport_layer.sv.tpl,$(IDMA_DB_FILES),$*,,$@,$(IDMA_VIDMA_IDS))

$(IDMA_RTL_DIR)/idma_legalizer_%.sv: $(IDMA_GEN) $(IDMA_GEN_SRC) $(IDMA_ROOT)/src/backend/tpl/idma_legalizer.sv.tpl $(IDMA_DB_FILES)
$(call idma_gen,legalizer,$(IDMA_ROOT)/src/backend/tpl/idma_legalizer.sv.tpl,$(IDMA_DB_FILES),$*,,$@)
$(IDMA_RTL_DIR)/idma_legalizer_%.sv: $(IDMA_GEN) $(IDMA_GEN_SRC) $(IDMA_ROOT)/src/backend/tpl/idma_legalizer.sv.tpl $(IDMA_DB_FILES) $(IDMA_VIDMA_STAMP)
$(call idma_gen,legalizer,$(IDMA_ROOT)/src/backend/tpl/idma_legalizer.sv.tpl,$(IDMA_DB_FILES),$*,,$@,$(IDMA_VIDMA_IDS))

$(IDMA_RTL_DIR)/idma_backend_%.sv: $(IDMA_GEN) $(IDMA_GEN_SRC) $(IDMA_RTL_DIR)/idma_legalizer_%.sv $(IDMA_RTL_DIR)/idma_transport_layer_%.sv $(IDMA_ROOT)/src/backend/tpl/idma_backend.sv.tpl $(IDMA_DB_FILES)
$(call idma_gen,backend,$(IDMA_ROOT)/src/backend/tpl/idma_backend.sv.tpl,$(IDMA_DB_FILES),$*,,$@)
$(IDMA_RTL_DIR)/idma_backend_%.sv: $(IDMA_GEN) $(IDMA_GEN_SRC) $(IDMA_RTL_DIR)/idma_legalizer_%.sv $(IDMA_RTL_DIR)/idma_transport_layer_%.sv $(IDMA_ROOT)/src/backend/tpl/idma_backend.sv.tpl $(IDMA_DB_FILES) $(IDMA_VIDMA_STAMP)
$(call idma_gen,backend,$(IDMA_ROOT)/src/backend/tpl/idma_backend.sv.tpl,$(IDMA_DB_FILES),$*,,$@,$(IDMA_VIDMA_IDS))

$(IDMA_RTL_DIR)/idma_backend_synth_%.sv: $(IDMA_GEN) $(IDMA_GEN_SRC) $(IDMA_RTL_DIR)/idma_backend_%.sv $(IDMA_ROOT)/src/backend/tpl/idma_backend_synth.sv.tpl $(IDMA_DB_FILES)
$(call idma_gen,synth_wrapper,$(IDMA_ROOT)/src/backend/tpl/idma_backend_synth.sv.tpl,$(IDMA_DB_FILES),$*,,$@)
Expand All @@ -151,6 +170,7 @@ idma_rtl_clean:
rm -f $(IDMA_VSIM_DIR)/wave/*.do
rm -f $(IDMA_RTL_DIR)/include/idma/tracer.svh
rm -rf $(IDMA_RTL_DIR)/include/idma
rm -f $(IDMA_VIDMA_STAMP)

# assemble the required files
IDMA_INCLUDE_ALL += $(IDMA_RTL_DIR)/include/idma/tracer.svh
Expand Down Expand Up @@ -304,7 +324,7 @@ IDMA_PICKLE_ALL += $(IDMA_PICKLE_DIR)/idma_mp_midend_synth.sv
$(IDMA_VSIM_DIR)/compile_%.tcl: $(IDMA_BENDER_FILES) $(IDMA_FULL_TB) $(IDMA_FULL_RTL) $(IDMA_INCLUDE_ALL) $(IDMA_WAVE_ALL)
echo 'set ROOT [file normalize [file dirname [info script]]/../../..]' > $@
set -o pipefail; $(BENDER) script vsim --vlog-arg="$(IDMA_VLOG_ARGS)" \
-t sim -t test -t idma_test -t synth -t rtl -t asic -t snitch_cluster -t split_rtl \
-t sim -t test -t idma_test -t synth -t rtl -t asic -t snitch_cluster \
--top $* | grep -v "set ROOT" >> $@
echo >> $@

Expand All @@ -329,13 +349,63 @@ endef
$(IDMA_VSIM_DIR)/compile.tcl: $(IDMA_BENDER_FILES) $(IDMA_FULL_TB) $(IDMA_FULL_RTL) $(IDMA_INCLUDE_ALL) $(IDMA_WAVE_ALL)
$(call idma_generate_vsim, $@, -t sim -t test -t idma_test -t synth -t rtl -t asic -t snitch_cluster,../../..)

# Standalone self-checking transpose-engine regression (DPI-C golden, no backend deps).
# Run with the Questa SEPP wrapper, e.g.:
# make idma_sim_tb_idma_otf_transpose VSIM="questa-2023.4 vsim" VLOG="questa-2023.4 vlog" VLIB="questa-2023.4 vlib"
IDMA_OTF_TP_RTL := $(abspath $(IDMA_ROOT)/src/backend/idma_otf_transpose.sv)
IDMA_OTF_TP_TB := $(abspath $(IDMA_ROOT)/test/tb_idma_otf_transpose.sv)
IDMA_OTF_TP_DPI := $(abspath $(IDMA_ROOT)/test/idma_transpose_dpi.c)
IDMA_OTF_TP_DIR := $(abspath $(IDMA_VSIM_DIR))/otf_transpose

.PHONY: idma_sim_tb_idma_otf_transpose
idma_sim_tb_idma_otf_transpose:
mkdir -p $(IDMA_OTF_TP_DIR)
cd $(IDMA_OTF_TP_DIR); $(VLIB) work
cd $(IDMA_OTF_TP_DIR); $(VLOG) -sv $(IDMA_OTF_TP_DPI)
cd $(IDMA_OTF_TP_DIR); $(VLOG) -sv -svinputport=compat -timescale "1ns/1fs" $(IDMA_OTF_TP_RTL) $(IDMA_OTF_TP_TB)
# the TB sweeps the geometry list internally; one run per StrbWidth x FullDuplex
cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=8 -gFullDuplex=1 tb_idma_otf_transpose +BP -do "run -all; quit"
cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=8 -gFullDuplex=0 tb_idma_otf_transpose +BP -do "run -all; quit"
cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=64 -gFullDuplex=1 tb_idma_otf_transpose +BP -do "run -all; quit"
cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=64 -gFullDuplex=0 tb_idma_otf_transpose +BP -do "run -all; quit"

# Multi-tile transpose via the ND midend -> rw_axi backend -> axi_sim_mem.
# Run with the Questa SEPP wrapper: make idma_sim_tb_idma_transpose_nd VSIM="questa-2023.4 vsim"
# These tests need the compute engine: build rw_axi with compute (stamp regens).
idma_sim_tb_idma_transpose_nd idma_sim_tb_idma_transpose_b2b: IDMA_VIDMA_IDS := rw_axi

.PHONY: idma_sim_tb_idma_transpose_nd
idma_sim_tb_idma_transpose_nd: $(IDMA_VSIM_DIR)/compile.tcl
cd $(IDMA_VSIM_DIR); $(VSIM) -c -do "source compile.tcl; quit"
# the TB sweeps the geometry list internally; one run per bus width
cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc -gDataWidth=32 tb_idma_transpose_nd -do "run -all; quit"
cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc -gDataWidth=64 tb_idma_transpose_nd -do "run -all; quit"

# Back-to-back regressions: the ND midend must reload each new transfer's base
# address (it does, for a protocol-compliant producer that drops nd_req_valid on
# accept). tb_idma_nd_midend_b2b checks the midend's burst-address sequence under
# backpressure; tb_idma_transpose_b2b checks two end-to-end transposes to distinct
# destinations. Run with the Questa SEPP wrapper.
.PHONY: idma_sim_tb_idma_nd_midend_b2b
idma_sim_tb_idma_nd_midend_b2b: $(IDMA_VSIM_DIR)/compile.tcl
cd $(IDMA_VSIM_DIR); $(VSIM) -c -do "source compile.tcl; quit"
cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc tb_idma_nd_midend_b2b -do "run -all; quit"

.PHONY: idma_sim_tb_idma_transpose_b2b
idma_sim_tb_idma_transpose_b2b: $(IDMA_VSIM_DIR)/compile.tcl
cd $(IDMA_VSIM_DIR); $(VSIM) -c -do "source compile.tcl; quit"
# the TB sweeps the geometry list internally; one run per bus width
cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc -gDataWidth=32 tb_idma_transpose_b2b -do "run -all; quit"
cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc -gDataWidth=64 tb_idma_transpose_b2b -do "run -all; quit"

.PHONY: idma_sim_tb_idma_rt_midend

idma_sim_tb_idma_rt_midend: $(IDMA_VSIM_DIR)/compile.tcl
cd $(IDMA_VSIM_DIR); $(VSIM) -c -do "source compile.tcl; quit"
cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc tb_idma_rt_midend -do "run -all; quit"

idma_sim_clean:
rm -rf $(IDMA_OTF_TP_DIR)
rm -rf $(IDMA_VSIM_DIR)/compile.tcl
rm -rf $(IDMA_VSIM_DIR)/work
rm -f $(IDMA_VSIM_DIR)/dma_trace_*
Expand Down
10 changes: 9 additions & 1 deletion src/backend/idma_axi_write.sv
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ module idma_axi_write #(
/// Valid from buffer
input strb_t buffer_out_valid_i,
/// Ready to buffer
output strb_t buffer_out_ready_o
output strb_t buffer_out_ready_o,
/// External write-strobe mask (ANDed into wstrb); tie to '1 when unused
input strb_t mask_ext_i,
/// Pulses when a write beat is accepted on the bus (strobe-independent)
output logic w_beat_done_o
);
// offsets needed for masks to empty buffer
strb_t w_first_mask;
Expand Down Expand Up @@ -141,6 +145,8 @@ module idma_axi_write #(
if (w_dp_req_i.tailer != '0 & last_w) begin
mask_out = mask_out & w_last_mask;
end
// external mask (OTF compute)
mask_out = mask_out & mask_ext_i;
end


Expand Down Expand Up @@ -170,6 +176,8 @@ module idma_axi_write #(
// write happening: both the bus (w_ready) and the buffer (ready_to_write) is high
assign write_happening = ready_to_write & write_rsp_i.w_ready;

assign w_beat_done_o = write_happening;

// the main buffer is conditionally to the write mask popped
assign buffer_out_ready_o = write_happening ? mask_out : '0;

Expand Down
100 changes: 100 additions & 0 deletions src/backend/idma_otf_compute.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2026 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51

// Authors:
// - Daniel Keller <dankeller@iis.ee.ethz.ch>

/// On-the-fly compute dispatcher: latches the per-transfer compute options
/// and dispatches one op per transfer to its sub-unit.
module idma_otf_compute #(
/// Byte lanes per beat (= DataWidth/8)
parameter int unsigned StrbWidth = 32'd8,
/// Compile-time per-op feature enables (value rendered by the generator)
parameter idma_pkg::compute_enable_t ComputeEnable = '0,
/// Transpose engine duplex (1: two banks full rate, 0: one bank half area)
parameter bit TransposeFullDuplex = 1'b1
) (
input logic clk_i,
input logic rst_ni,

/// Per-transfer compute config; valid only while `cfg_valid_i`
input idma_pkg::compute_options_t compute_i,
input logic cfg_valid_i,
/// A supported compute op is armed for this transfer
output logic active_o,

/// Input beat stream (from the dataflow buffer)
input logic [StrbWidth-1:0][7:0] data_i,
input logic valid_i,
output logic in_ready_o,

/// Output beat stream (computed) with per-byte strobe for edge masking
output logic [StrbWidth-1:0][7:0] data_o,
output logic [StrbWidth-1:0] strb_o,
output logic valid_o,
input logic ready_i
);

// config latch with first-beat bypass
idma_pkg::compute_options_t latched_q, eff_compute;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) latched_q <= '0;
else if (cfg_valid_i) latched_q <= compute_i;
end
assign eff_compute = cfg_valid_i ? compute_i : latched_q;

// per-op select
logic sel_transpose;
assign sel_transpose = eff_compute.enable &
(eff_compute.op == idma_pkg::COMPUTE_TRANSPOSE) & ComputeEnable.transpose;

assign active_o = sel_transpose;

// transpose sub-unit
logic [StrbWidth-1:0][7:0] tp_data;
logic [StrbWidth-1:0] tp_strb;
logic tp_valid, tp_in_ready;

if (ComputeEnable.transpose) begin : gen_transpose
idma_otf_transpose #(
.StrbWidth ( StrbWidth ),
.DimWidth ( idma_pkg::TransposeDimWidth ),
.FullDuplex ( TransposeFullDuplex )
) i_idma_otf_transpose (
.clk_i,
.rst_ni,
.clear_i ( ~sel_transpose ),
.transp_mode_i ( eff_compute.params.transpose.mode ),
.tensor_size_m_i ( eff_compute.params.transpose.tensor_m ),
.tensor_size_n_i ( eff_compute.params.transpose.tensor_n ),
.data_i ( data_i ),
.valid_i ( valid_i & sel_transpose ),
.ready_o ( tp_in_ready ),
.data_o ( tp_data ),
.strb_o ( tp_strb ),
.valid_o ( tp_valid ),
.ready_i ( ready_i & sel_transpose )
);
end else begin : gen_no_transpose
assign tp_data = '0; assign tp_strb = '0; assign tp_valid = 1'b0; assign tp_in_ready = 1'b0;
end

// output dispatch
always_comb begin
data_o = '0;
strb_o = '0;
valid_o = 1'b0;
in_ready_o = 1'b0;
unique case (1'b1)
sel_transpose: begin
data_o = tp_data;
strb_o = tp_strb;
valid_o = tp_valid;
in_ready_o = tp_in_ready;
end
default: ;
endcase
end

endmodule : idma_otf_compute
Loading
Loading