From 240d09007b8c75c0c02aa2297061ce72cda3a8d7 Mon Sep 17 00:00:00 2001 From: Sergio Mazzola Date: Thu, 31 Jul 2025 19:47:52 +0200 Subject: [PATCH 01/29] rtl,verif: Fix build flow and RTL compilation issues --- Makefile | 2 +- modelsim/Makefile | 2 +- verif/tb/tb_datamover_top_wrap.sv | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index e0e3f32..4c71483 100644 --- a/Makefile +++ b/Makefile @@ -114,4 +114,4 @@ check-bender: $(BENDER_INSTALL_DIR)/bender: mkdir -p $(BENDER_INSTALL_DIR) && cd $(BENDER_INSTALL_DIR) && \ - curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- $(BENDER_VERSION) \ No newline at end of file + curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- $(BENDER_VERSION) diff --git a/modelsim/Makefile b/modelsim/Makefile index f6ce7b9..db7baf7 100644 --- a/modelsim/Makefile +++ b/modelsim/Makefile @@ -28,4 +28,4 @@ build: cd $(buildpath) && $(VSIM) -c -do 'source compile.tcl; quit' sim_tb_datamover_top_wrap: lib build - cd $(buildpath) && $(VSIM) $(VSIM_FLAGS) -do 'source ../sim_tb_datamover_top_wrap.tcl' \ No newline at end of file + cd $(buildpath) && $(VSIM) $(VSIM_FLAGS) -do 'source ../sim_tb_datamover_top_wrap.tcl' diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index 5f102a2..735f183 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -290,4 +290,4 @@ import tb_package::*; end : main_execution -endmodule // tb_datamover_top_wrap \ No newline at end of file +endmodule // tb_datamover_top_wrap From df9fa61e6872101601b0df4c9c5c24178ad10334 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Thu, 16 Oct 2025 18:05:53 +0200 Subject: [PATCH 02/29] Fixing RTL and simulation issues --- Bender.yml | 1 + verif/tb/tb_datamover_top_wrap.sv | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Bender.yml b/Bender.yml index a8d3a46..8a904bc 100644 --- a/Bender.yml +++ b/Bender.yml @@ -3,6 +3,7 @@ package: authors: - "Francesco Conti " - "Sergio Mazzola " + - "Cyrill Durrer " dependencies: hwpe-stream: { git: "https://github.com/pulp-platform/hwpe-stream.git", rev: 40ab0fe1433dc080a49aa1926ce09df9ab3f5fb5 } # branch: prasadar/multi-precision diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index 735f183..469608c 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -13,7 +13,8 @@ /* * Authors: Sergio Mazzola - * Arpan Suravi Prasad s + * Arpan Suravi Prasad + * Cyrill Durrer */ @@ -233,9 +234,10 @@ import tb_package::*; $readmemh(STIMULI_PATH, tb_datamover_top_wrap.i_testbench_memory.memory); // soft clear - periph_write(datamover_package::DATAMOVER_SOFT_CLEAR, datamover_package::HWPE_REGISTER_OFFS, 32'habcdefab, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_SOFT_CLEAR, datamover_package::HWPE_REGISTER_OFFS, 32'habcdefab, clk_i, periph_bus); #(100*TCP); + $display("[%0t] acquiring job...\n", $time); // acquire job $info("Acquiring job...\n"); while(status !== 32'h00) @@ -287,7 +289,7 @@ import tb_package::*; ); $finish; - + end : main_execution endmodule // tb_datamover_top_wrap From 1c73e92a3ebea5173dea86adb079f72a6ccc570e Mon Sep 17 00:00:00 2001 From: cdurrer Date: Tue, 21 Oct 2025 10:05:22 +0200 Subject: [PATCH 03/29] [mk] Make vsim GUI optional --- ReadMe.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ modelsim/Makefile | 7 +++++++ 2 files changed, 53 insertions(+) create mode 100644 ReadMe.md diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..eae3f30 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,46 @@ +# Project Build and Simulation Guide + +This ReadMe provides instructions on how to set up and run the standalone simulation using `make` commands. + +## Available Make Commands + +### 1. To clone the dependencies, run: +```sh +make bender +``` + +### 2. Generate Stimuli and Golden Files +To generate the stimuli and golden reference using a Python script, run: +```sh +make stimuli +``` + +### 3. Create Compilation Script +To create a compilation script for compiling the hardware, run: +```sh +make sim-script +``` + +### 4. Simulate the Design +To simulate the RTL, execute: +```sh +make sim +``` +By default QuestaSim GUI is active. You can simulate the RTL in CLI mode with `GUI=0 make sim`. + +## Test Results +If the tests pass successfully, you should see the following message displayed at the end: +``` +PASSED!!!! +``` + +## Contributors +- Francesco Conti, University of Bologna (*f.conti@unibo.it*) +- Arpan Suravi Prasad, ETH Zurich (*prasadar@iis.ee.ethz.ch*) + +## License +This repository makes use of two licenses: +- for all *software*: Apache License Version 2.0 +- for all *hardware*: Solderpad Hardware License Version 0.51 + +For further information have a look at the license files: `LICENSE.hw`, `LICENSE.sw` diff --git a/modelsim/Makefile b/modelsim/Makefile index db7baf7..b05e5ea 100644 --- a/modelsim/Makefile +++ b/modelsim/Makefile @@ -21,6 +21,13 @@ else VSIM_FLAGS += -c endif +VSIM_FLAGS ?= +ifeq ($(GUI), 1) + VSIM_FLAGS += -gui +else + VSIM_FLAGS += -c +endif + lib: cd $(buildpath) && $(VLIB) work && $(VMAP) work work From 85a04596a73c37fcc36645ddf09574fc34d8296d Mon Sep 17 00:00:00 2001 From: cdurrer Date: Tue, 21 Oct 2025 10:16:53 +0200 Subject: [PATCH 04/29] [tb] fixed packed register problem --- verif/tb/tb_datamover_top_wrap.sv | 2 +- verif/tb/tb_package.sv | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index 469608c..30c03f5 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -237,7 +237,7 @@ import tb_package::*; periph_write(datamover_package::DATAMOVER_SOFT_CLEAR, datamover_package::HWPE_REGISTER_OFFS, 32'habcdefab, clk_i, periph_bus); #(100*TCP); - $display("[%0t] acquiring job...\n", $time); + $info("[%0t] Acquiring job...\n", $time); // acquire job $info("Acquiring job...\n"); while(status !== 32'h00) diff --git a/verif/tb/tb_package.sv b/verif/tb/tb_package.sv index 391549f..2209189 100644 --- a/verif/tb/tb_package.sv +++ b/verif/tb/tb_package.sv @@ -1,4 +1,4 @@ -/* +/* * tb_package.sv * Arpan Suravi Prasad * @@ -13,8 +13,14 @@ * specific language governing permissions and limitations under the License. */ package tb_package; - /* Configuration */ + // ATI timing parameters + timeunit 1ps; + timeprecision 1ps; + localparam TCP = 1.0ns; // clock period, 1 GHz clock + localparam TA = 0.2ns; // application time + localparam TT = 0.8ns; // test time + /* Configuration */ localparam int ADDR_WIDTH = 32; localparam int PERIPH_ID = 10; localparam int MEMORY_SIZE= `STIM_MEM_SIZE; @@ -35,12 +41,8 @@ package tb_package; localparam int unsigned WORD_WIDTH = NUM_ELEM_WORD * ELEM_WIDTH; // should correspond to bank width localparam int unsigned BANDWIDTH_WORDS = BANDWIDTH / WORD_WIDTH; - // // ATI timing parameters. - timeunit 1ps; - timeprecision 1ps; - localparam TCP = 1.0ns; // clock period, 1 GHz clock - localparam TA = 0.2ns; // application time - localparam TT = 0.8ns; // test time + localparam int ID = 10; + localparam int MEMORY_SIZE=256*1024; typedef struct{ logic req; @@ -161,7 +163,7 @@ package tb_package; status = 0; // Assume pass initially - + // Open the golden reference file for reading file = $fopen(golden_fname, "r"); if (file == 0) begin @@ -204,6 +206,6 @@ package tb_package; end endtask - -endpackage \ No newline at end of file + +endpackage From d3e32af131342299d655a4120d0955dc5e41a2ac Mon Sep 17 00:00:00 2001 From: cdurrer Date: Tue, 21 Oct 2025 15:03:08 +0200 Subject: [PATCH 05/29] [mk,tb] added notes and debug outputs --- verif/python/generate_stimuli.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/verif/python/generate_stimuli.py b/verif/python/generate_stimuli.py index 1253c4a..7cef5d6 100644 --- a/verif/python/generate_stimuli.py +++ b/verif/python/generate_stimuli.py @@ -18,14 +18,23 @@ def generate_addresses(start, d0_stride, d0_length, d1_stride, d1_length, transa addr = start count = 0 + print(f"Generating addresses starting from {hex(start)} with N={N} words per transaction") + print(f"d0_stride: {d0_stride}, d0_length: {d0_length}, d1_stride: {d1_stride}, d1_length: {d1_length}") + for d1 in range(d1_length): addr_d1 = addr + d1 * d1_stride + print(f"Generating addresses for d1={d1} at {hex(addr_d1)}") for d0 in range(d0_length): - addr_d0 = addr_d1 + d0 * d0_stride + addr_d0 = addr_d1 + d0 * d0_stride # ToDo(cdurrer): shouldnt this be addr_d1* + d0 * d0_stride? if addr_d0 + N <= MEMORY_SIZE: # Ensure full block fits + block = [addr_d0 + i for i in range(N)] + print(f"Appending address block: {', '.join(hex(a) for a in block)}") addresses.append([addr_d0 + i for i in range(N)]) # Read/Write N words count += 1 + else: + print(f"Warning: Address block starting at {hex(addr_d0)} exceeds memory size. Skipping.") if count == transactions: # Stop when enough transactions are generated + print(f"Transaction limit reached! Generated total of {count} transactions.") return addresses return addresses @@ -36,6 +45,7 @@ def update_memory(memory, write_addresses, extracted_data): for addr, value in zip(addr_block, data_block): if addr < len(memory): memory[addr] = value # Write N words at a time + print(f"Writing value {value} to address {hex(addr)}") def write_file(output_dir, filename, content): """Write list content to a file.""" @@ -83,6 +93,8 @@ def main(): bandwidth_N = args.bandwidth_bits // WORD_SIZE_BITS + print(f"Memory Size: {MEMORY_SIZE} entries") + # Step 1: Generate initial memory memory = generate_random_hex_32bit(MEMORY_SIZE) From 9f664e4b1a3939b4ed65684436f07059edf67921 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Wed, 22 Oct 2025 14:43:12 +0200 Subject: [PATCH 06/29] [tb] extended golden model for Konark data layout conversion --- verif/python/generate_stimuli.py | 7 +- verif/python/generate_stimuli_konark.py | 161 ++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 verif/python/generate_stimuli_konark.py diff --git a/verif/python/generate_stimuli.py b/verif/python/generate_stimuli.py index 7cef5d6..4762324 100644 --- a/verif/python/generate_stimuli.py +++ b/verif/python/generate_stimuli.py @@ -9,6 +9,10 @@ def ceildiv(a, b): hex_length = ceildiv(WORD_SIZE_BITS, 4) # Each hex digit represents 4 bits return [f"{random.randint(0, 2**WORD_SIZE_BITS - 1):0{hex_length}X}" for _ in range(size)] +def generate_counting_hex_32bit(size): + """Generate counting series of 32-bit hex values (0, 1, 2, 3, ...).""" + return [f"{i:08X}" for i in range(size)] + def generate_addresses(start, d0_stride, d0_length, d1_stride, d1_length, transactions, N): """ Generate addresses ensuring total transactions match, processing N words per transaction. @@ -96,7 +100,8 @@ def main(): print(f"Memory Size: {MEMORY_SIZE} entries") # Step 1: Generate initial memory - memory = generate_random_hex_32bit(MEMORY_SIZE) + # memory = generate_random_hex_32bit(MEMORY_SIZE) + memory = generate_counting_hex_32bit(MEMORY_SIZE) # For debugging purposes # Convert addresses from element-addressing (e.g., byte-addressing) to word-addressing elem_offset_bit = (args.num_elem_word).bit_length() - 1 diff --git a/verif/python/generate_stimuli_konark.py b/verif/python/generate_stimuli_konark.py new file mode 100644 index 0000000..d705c35 --- /dev/null +++ b/verif/python/generate_stimuli_konark.py @@ -0,0 +1,161 @@ +import random +import argparse +import os + +def generate_random_hex_32bit(size): + """Generate random 32-bit hex values.""" + return [f"{random.randint(0, 2**32 - 1):08X}" for _ in range(size)] + +def generate_counting_words_hex_32bit(size): + """Generate counting series of 32-bit hex values (0, 1, 2, 3, ...).""" + return [f"{i:08X}" for i in range(size)] + +def generate_counting_bytes_hex_32bit(size): + """Generate counting series of 32-bit hex values (0, 1, 2, 3, ...).""" + return [f"{(i*4):08X}" for i in range(size)] + +# def generate_counting_bytes_hex_32bit(size): +# """Generate counting series packed as 32-bit hex values (byte-wise counting in little-endian format). +# Example: 03020100, 07060504, 0B0A0908, ... +# """ +# result = [] +# for i in range(size): +# # Pack 4 consecutive bytes into a 32-bit word (little-endian) +# byte0 = (i * 4 + 0) & 0xFF +# byte1 = (i * 4 + 1) & 0xFF +# byte2 = (i * 4 + 2) & 0xFF +# byte3 = (i * 4 + 3) & 0xFF +# # Pack as little-endian: [byte3][byte2][byte1][byte0] +# word = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0 +# result.append(f"{word:08X}") +# return result + +def generate_addresses(start, d0_stride, d0_length, d1_stride, d1_length, d2_stride, d2_length, transactions, N): + """ + Generate addresses ensuring total transactions match, processing N words per transaction. + Strides are adjusted with `>> 2` to align with word-based addressing. + """ + addresses = [] + addr = start + count = 0 + + print(f"Generating addresses starting from {hex(start)} with N={N} words per transaction") + print(f"d0_stride: {d0_stride}, d0_length: {d0_length}, d1_stride: {d1_stride}, d1_length: {d1_length}, d2_stride: {d2_stride}, d2_length: {d2_length}") + + for d2 in range(d2_length): + addr_d2 = addr + d2 * d2_stride + # print(f"Generating addresses for d2={d2} at {hex(addr_d2)}") + print(f"Generating addresses for d2={d2} at {hex(4*addr_d2)} [Byte Address]") + for d1 in range(d1_length): + addr_d1 = addr_d2 + d1 * d1_stride + # print(f"Generating addresses for d1={d1} at {hex(addr_d1)}") + print(f" Generating addresses for d1={d1} at {hex(4*addr_d1)} [Byte Address]") + for d0 in range(d0_length): + addr_d0 = addr_d1 + d0 * d0_stride # ToDo(cdurrer): shouldnt this be addr_d1* + d0 * d0_stride? + if addr_d0 + N <= MEMORY_SIZE: # Ensure full block fits + block = [addr_d0 + i for i in range(N)] + # print(f"Appending address block: {', '.join(hex(a) for a in block)}") + print(f" Appending address block: {', '.join(hex(4*a) for a in block)} [Byte Addresses]") + addresses.append([addr_d0 + i for i in range(N)]) # Read/Write N words + count += 1 + else: + # print(f"Warning: Address block starting at {hex(addr_d0)} exceeds memory size. Skipping.") + print(f" Warning: Address block starting at {hex(4*addr_d0)} exceeds memory size. Skipping. [Byte Address]") + if count == transactions: # Stop when enough transactions are generated + print(f"Transaction limit reached! Generated total of {count} transactions.") + return addresses + + return addresses + +def update_memory(memory, write_addresses, extracted_data): + """Update memory with extracted data using generated write addresses, processing N words at a time.""" + for addr_block, data_block in zip(write_addresses, extracted_data): + for addr, value in zip(addr_block, data_block): + if addr < len(memory): + memory[addr] = value # Write N words at a time + print(f"Writing value {value} to address {hex(addr)}") + +def write_file(output_dir, filename, content): + """Write list content to a file.""" + os.makedirs(output_dir, exist_ok=True) # Ensure directory exists + filepath = os.path.join(output_dir, filename) + with open(filepath, "w") as file: + file.write("\n".join(content) + "\n") + +def main(): + # Parse command-line arguments + parser = argparse.ArgumentParser(description="Memory Read/Write Simulation with Word-Aligned Strides") + parser.add_argument("--mem_size", type=int, default=0x30, help="Memory size in entries") + parser.add_argument("--read_base_addr", type=int, default=0x00, help="Base address for read operations") + parser.add_argument("--write_base_addr", type=int, default=0x20, help="Base address for write operations") + parser.add_argument("--read_d0_stride", type=int, default=4, help="Stride for d0 read (in bytes)") + parser.add_argument("--read_d1_stride", type=int, default=16, help="Stride for d1 read (in bytes)") + parser.add_argument("--read_d0_length", type=int, default=4, help="Length for d0 read") + parser.add_argument("--read_d1_length", type=int, default=4, help="Length for d1 read") + parser.add_argument("--write_d0_stride", type=int, default=4, help="Stride for d0 write (in bytes)") + parser.add_argument("--write_d1_stride", type=int, default=16, help="Stride for d1 write (in bytes)") + parser.add_argument("--write_d0_length", type=int, default=4, help="Length for d0 write") + parser.add_argument("--write_d1_length", type=int, default=4, help="Length for d1 write") + parser.add_argument("--bandwidth_N", type=int, default=4, help="Number of words per transaction") + parser.add_argument("--output_dir", type=str, default="output", help="Directory for storing output files") + + args = parser.parse_args() + + global MEMORY_SIZE + MEMORY_SIZE = args.mem_size # Set global memory size + + print(f"Memory Size: {MEMORY_SIZE} entries") + + # Step 1: Generate initial memory + # memory = generate_random_hex_32bit(MEMORY_SIZE) + memory = generate_counting_bytes_hex_32bit(MEMORY_SIZE) # For debugging purposes + + # Convert byte-based to word-based ( >> 2) + + args.write_base_addr = args.write_base_addr >> 2 + args.read_base_addr = args.read_base_addr >> 2 + args.write_d0_stride = args.write_d0_stride >> 2 + args.write_d1_stride = args.write_d1_stride >> 2 + args.read_d0_stride = args.read_d0_stride >> 2 + args.read_d1_stride = args.read_d1_stride >> 2 + + read_d2_length = 4 + read_d2_stride = 64 >> 2 + write_d2_length = 4 + write_d2_stride = 4352 >> 2 + + # Step 2: Generate read addresses (Word-aligned) + read_transactions = args.read_d0_length * args.read_d1_length * read_d2_length + read_addresses = generate_addresses(args.read_base_addr, args.read_d0_stride, args.read_d0_length, + args.read_d1_stride, args.read_d1_length, read_d2_stride, read_d2_length, read_transactions, args.bandwidth_N) + + # Step 3: Extract memory values based on read addresses + extracted_data = [[memory[addr] for addr in block] for block in read_addresses if all(addr < MEMORY_SIZE for addr in block)] + + # Step 4: Save initial memory + write_file(args.output_dir, "initial_memory.txt", memory) + + # Step 5: Save debug info (addresses read and values extracted) + debug_info = [ + f"Read Block: {', '.join(hex(addr) for addr in block)} -> Data: {', '.join(memory[addr] for addr in block)}" + for block in read_addresses if all(addr < MEMORY_SIZE for addr in block) + ] + write_file(args.output_dir, "debug_values.txt", debug_info) + + # Step 6: Generate write addresses (Word-aligned) + write_transactions = read_transactions + write_addresses = generate_addresses( + args.write_base_addr, args.write_d0_stride, args.write_d0_length, + args.write_d1_stride, args.write_d1_length, write_d2_stride, write_d2_length, write_transactions, args.bandwidth_N + ) + + # Step 7: Update memory with extracted data at write addresses + update_memory(memory, write_addresses, extracted_data) + + # Step 8: Save updated memory + write_file(args.output_dir, "updated_memory.txt", memory) + + print(f"Files generated in '{args.output_dir}': initial_memory.txt, debug_values.txt, updated_memory.txt") + +if __name__ == "__main__": + main() From 45be765d6758957681363a26ebfbc0e5a6b7c965 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Wed, 22 Oct 2025 14:54:33 +0200 Subject: [PATCH 07/29] [tb] separated 2d and 3d function in golden model --- verif/python/generate_stimuli_konark.py | 48 ++++++++++++++++++++----- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/verif/python/generate_stimuli_konark.py b/verif/python/generate_stimuli_konark.py index d705c35..dafce2a 100644 --- a/verif/python/generate_stimuli_konark.py +++ b/verif/python/generate_stimuli_konark.py @@ -30,7 +30,7 @@ def generate_counting_bytes_hex_32bit(size): # result.append(f"{word:08X}") # return result -def generate_addresses(start, d0_stride, d0_length, d1_stride, d1_length, d2_stride, d2_length, transactions, N): +def generate_addresses_2d(start, d0_stride, d0_length, d1_stride, d1_length, transactions, N): """ Generate addresses ensuring total transactions match, processing N words per transaction. Strides are adjusted with `>> 2` to align with word-based addressing. @@ -39,7 +39,37 @@ def generate_addresses(start, d0_stride, d0_length, d1_stride, d1_length, d2_str addr = start count = 0 - print(f"Generating addresses starting from {hex(start)} with N={N} words per transaction") + print(f"Generating addresses (2D) starting from {hex(start)} with N={N} words per transaction") + print(f"d0_stride: {d0_stride}, d0_length: {d0_length}, d1_stride: {d1_stride}, d1_length: {d1_length}") + + for d1 in range(d1_length): + addr_d1 = addr + d1 * d1_stride + print(f"Generating addresses for d1={d1} at {hex(addr_d1)}") + for d0 in range(d0_length): + addr_d0 = addr_d1 + d0 * d0_stride # ToDo(cdurrer): shouldnt this be addr_d1* + d0 * d0_stride? + if addr_d0 + N <= MEMORY_SIZE: # Ensure full block fits + block = [addr_d0 + i for i in range(N)] + print(f"Appending address block: {', '.join(hex(a) for a in block)}") + addresses.append([addr_d0 + i for i in range(N)]) # Read/Write N words + count += 1 + else: + print(f"Warning: Address block starting at {hex(addr_d0)} exceeds memory size. Skipping.") + if count == transactions: # Stop when enough transactions are generated + print(f"Transaction limit reached! Generated total of {count} transactions.") + return addresses + + return addresses + +def generate_addresses_3d(start, d0_stride, d0_length, d1_stride, d1_length, d2_stride, d2_length, transactions, N): + """ + Generate addresses ensuring total transactions match, processing N words per transaction. + Strides are adjusted with `>> 2` to align with word-based addressing. + """ + addresses = [] + addr = start + count = 0 + + print(f"Generating addresses (3D) starting from {hex(start)} with N={N} words per transaction") print(f"d0_stride: {d0_stride}, d0_length: {d0_length}, d1_stride: {d1_stride}, d1_length: {d1_length}, d2_stride: {d2_stride}, d2_length: {d2_length}") for d2 in range(d2_length): @@ -126,8 +156,10 @@ def main(): # Step 2: Generate read addresses (Word-aligned) read_transactions = args.read_d0_length * args.read_d1_length * read_d2_length - read_addresses = generate_addresses(args.read_base_addr, args.read_d0_stride, args.read_d0_length, - args.read_d1_stride, args.read_d1_length, read_d2_stride, read_d2_length, read_transactions, args.bandwidth_N) + read_addresses = generate_addresses_2d(args.read_base_addr, args.read_d0_stride, args.read_d0_length, + args.read_d1_stride, args.read_d1_length, read_transactions, args.bandwidth_N) + # read_addresses = generate_addresses_3d(args.read_base_addr, args.read_d0_stride, args.read_d0_length, + # args.read_d1_stride, args.read_d1_length, read_d2_stride, read_d2_length, read_transactions, args.bandwidth_N) # Step 3: Extract memory values based on read addresses extracted_data = [[memory[addr] for addr in block] for block in read_addresses if all(addr < MEMORY_SIZE for addr in block)] @@ -144,10 +176,10 @@ def main(): # Step 6: Generate write addresses (Word-aligned) write_transactions = read_transactions - write_addresses = generate_addresses( - args.write_base_addr, args.write_d0_stride, args.write_d0_length, - args.write_d1_stride, args.write_d1_length, write_d2_stride, write_d2_length, write_transactions, args.bandwidth_N - ) + write_addresses = generate_addresses_2d(args.write_base_addr, args.write_d0_stride, args.write_d0_length, + args.write_d1_stride, args.write_d1_length, write_transactions, args.bandwidth_N) + # write_addresses = generate_addresses_3d(args.write_base_addr, args.write_d0_stride, args.write_d0_length, + # args.write_d1_stride, args.write_d1_length, write_d2_stride, write_d2_length, write_transactions, args.bandwidth_N) # Step 7: Update memory with extracted data at write addresses update_memory(memory, write_addresses, extracted_data) From 6e749134f637832345d1348d1c77e96aa9aab8a5 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Wed, 22 Oct 2025 16:14:11 +0200 Subject: [PATCH 08/29] [tb] fixed issue introduced while rebasing to smazzola/multi-precision branch --- verif/tb/tb_package.sv | 3 --- 1 file changed, 3 deletions(-) diff --git a/verif/tb/tb_package.sv b/verif/tb/tb_package.sv index 2209189..a802fac 100644 --- a/verif/tb/tb_package.sv +++ b/verif/tb/tb_package.sv @@ -41,9 +41,6 @@ package tb_package; localparam int unsigned WORD_WIDTH = NUM_ELEM_WORD * ELEM_WIDTH; // should correspond to bank width localparam int unsigned BANDWIDTH_WORDS = BANDWIDTH / WORD_WIDTH; - localparam int ID = 10; - localparam int MEMORY_SIZE=256*1024; - typedef struct{ logic req; logic gnt; From 70506c17486ae193fa5a0d0b2e49d6e2831c9fbe Mon Sep 17 00:00:00 2001 From: cdurrer Date: Thu, 30 Oct 2025 09:20:37 +0100 Subject: [PATCH 09/29] [tb] Worked on transpose mode and golden model --- .gitignore | 3 +- Makefile | 4 +- README.md | 5 +- config.mk | 66 +++++--- verif/python/generate_stimuli.py | 216 +++++++++++++++++++++----- verif/python/generate_stimuli_test.py | 198 +++++++++++++++++++++++ verif/tb/tb_datamover_top_wrap.sv | 24 +-- 7 files changed, 446 insertions(+), 70 deletions(-) create mode 100644 verif/python/generate_stimuli_test.py diff --git a/.gitignore b/.gitignore index 2ce44cf..2e38409 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ .bender +.github modelsim/vsim test/BUILD **.log **.txt **__pycache** verif/python/generated/** -install \ No newline at end of file +install diff --git a/Makefile b/Makefile index 4c71483..915478b 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ TESTBENCH_DEFINES += -DSTIM_WRITE_TOT_LENGTH=${STIM_WRITE_TOT_LENGTH} TESTBENCH_DEFINES += -DSTIM_MEM_SIZE=${STIM_MEM_SIZE} TESTBENCH_DEFINES += -DSTIM_TRANSP_MODE=${STIM_TRANSP_MODE} +TESTBENCH_DEFINES += -DSTIM_TRANSP_LEN=${STIM_TRANSP_LEN} TESTBENCH_DEFINES += -DBANDWIDTH=${BANDWIDTH} TESTBENCH_DEFINES += -DNUM_ELEM_WORD=${NUM_ELEM_WORD} @@ -79,7 +80,7 @@ clean-stimuli: rm -rf $(STIMULI_DIR) stimuli: clean-stimuli - python -m verif.python.generate_stimuli \ + python -m verif.python.generate_stimuli_test \ --mem_size $(STIM_MEM_SIZE) \ --read_base_addr $(STIM_READ_BASE_ADDR) \ --read_d0_stride $(STIM_READ_D0_STRIDE) \ @@ -95,6 +96,7 @@ stimuli: clean-stimuli --num_elem_word $(NUM_ELEM_WORD) \ --elem_width $(ELEM_WIDTH) \ --transp_mode $(STIM_TRANSP_MODE) \ + --transp_len $(STIM_TRANSP_LEN) \ --output_dir "verif/python/generated" # Bender diff --git a/README.md b/README.md index 1062fae..0dbad9b 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ The following parameters are used to generate the testbench stimuli and to confi `STIM_*_STRIDE` = stride between element across dimensions d0/d1 (element-addressed, e.g., stride d1 would be the distance in an element-addressed offset between A[row=0][col=0] and A[row=1][col=0]) `STIM_MEM_SIZE` = number of words of the testbench memory `STIM_TRANSP_MODE` = transposition mode to configure for the datamover (`3'b000` = none, `3'b001` = 1 elem, `3'b010` = 2 elem, `3'b100` = 4 elem) +`STIM_TRANSP_LEN` = transposition length (if set to 0: transp_len = BANDWIDTH_ALIGNED / ELEM_WIDTH) For the complete list of the datamover configuration registers, cf. `datamover_package.sv`. @@ -75,5 +76,5 @@ PASSED!!!! This repository makes use of two licenses: - for all *software*: Apache License Version 2.0 - for all *hardware*: Solderpad Hardware License Version 0.51 - -For further information have a look at the license files: `LICENSE.hw`, `LICENSE.sw` \ No newline at end of file + +For further information have a look at the license files: `LICENSE.hw`, `LICENSE.sw` diff --git a/config.mk b/config.mk index 97ffd27..e0060f1 100644 --- a/config.mk +++ b/config.mk @@ -9,28 +9,54 @@ # Datamover hw config # ####################### -BANDWIDTH ?= 128 # in bits -NUM_ELEM_WORD ?= 4 # number of element in a memory bank word (powers of 2) -ELEM_WIDTH ?= 8 # width of an element (e.g., a byte is 8 bits) +BANDWIDTH = 128 # in bits +WORD_WIDTH = 32 # in bits +ELEM_WIDTH = 8 # in bits +MEMORY_SIZE = 512 # in words + +TRANSP_MODE = 4 # 0 = none, 1 = 1 elem, 2 = 2 elem, 4 = 4 elem + +# Derived constants from basic parameters +# BANDWIDTH_WORDS := $(shell echo $$(($(BANDWIDTH) / $(WORD_WIDTH)))) # Number of words per bandwidth +BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH) / $(ELEM_WIDTH)))) # Number of elements per bandwidth +NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of elements per word ######################### # Stimuli configuration # ######################### -# Tb stimuli config -STIM_READ_BASE_ADDR ?= 0 # element-addressed -STIM_READ_D0_LENGTH ?= 4 -STIM_READ_D0_STRIDE ?= 16 # element-addressed -STIM_READ_D1_LENGTH ?= 4 -STIM_READ_D1_STRIDE ?= 32 # element-addressed -STIM_READ_TOT_LENGTH ?= 16 - -STIM_WRITE_BASE_ADDR ?= 256 # element-addressed -STIM_WRITE_D0_LENGTH ?= 4 -STIM_WRITE_D0_STRIDE ?= 64 # element-addressed -STIM_WRITE_D1_LENGTH ?= 4 -STIM_WRITE_D1_STRIDE ?= 64 # element-addressed -STIM_WRITE_TOT_LENGTH ?= 16 -STIM_MEM_SIZE ?= 65536 # in words - -STIM_TRANSP_MODE ?= 0 # 3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem +# Base matrix dimensions (in elements) +MATRIX_SIZE_D0 ?= 16 # Matrix width in elements +MATRIX_SIZE_D1 ?= 8 # Matrix height in elements + +# Derived stride calculations +# ELEM_STRIDE_D0 := $(ELEM_WIDTH) # Element-to-element stride in bits +# ROW_STRIDE_BYTES := $(shell echo $$(($(MATRIX_SIZE_D0) * $(ELEM_WIDTH) / 8))) # Bytes per row + +# ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (4*32b) +# N (bandwidth) consecutive words are read/written in one transaction +STIM_READ_BASE_ADDR ?= 0 # Element-addressed +STIM_READ_D0_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_D0) / $(BANDWIDTH_ELEMS)))) # Nof accesses with bandwidth BW per D0-transfer ("row") +STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # Elements +STIM_READ_D1_LENGTH ?= $(MATRIX_SIZE_D1) # Number of full D0-transfers ("rows") +STIM_READ_D1_STRIDE ?= $(MATRIX_SIZE_D0) # Elements -> manually compute "next row" stride +STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # Total memory accesses + +STIM_WRITE_BASE_ADDR ?= 128 # Element-addressed +STIM_WRITE_D0_LENGTH ?= $(STIM_READ_D1_LENGTH) # Transpose: read height becomes write width +STIM_WRITE_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # Bytes per transposed row +STIM_WRITE_D1_LENGTH ?= $(STIM_READ_D0_LENGTH) # Transpose: read width becomes write height +STIM_WRITE_D1_STRIDE ?= $(MATRIX_SIZE_D0) # Elements +STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read + +STIM_MEM_SIZE ?= $(MEMORY_SIZE) #65536 + +STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem +STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH + +# Debug: Print computed values (uncomment to see values during make) +$(info WORD_WIDTH=$(WORD_WIDTH)) +$(info BANDWIDTH_ELEMS=$(BANDWIDTH_ELEMS)) +$(info ROW_STRIDE_BYTES=$(ROW_STRIDE_BYTES)) +$(info STIM_READ_D0_LENGTH=$(STIM_READ_D0_LENGTH)) +$(info STIM_WRITE_D0_STRIDE=$(STIM_WRITE_D0_STRIDE)) diff --git a/verif/python/generate_stimuli.py b/verif/python/generate_stimuli.py index 4762324..5e6659d 100644 --- a/verif/python/generate_stimuli.py +++ b/verif/python/generate_stimuli.py @@ -11,9 +11,30 @@ def ceildiv(a, b): def generate_counting_hex_32bit(size): """Generate counting series of 32-bit hex values (0, 1, 2, 3, ...).""" - return [f"{i:08X}" for i in range(size)] + return [f"{(i*4):08X}" for i in range(size)] -def generate_addresses(start, d0_stride, d0_length, d1_stride, d1_length, transactions, N): +def generate_counting_hex_8bit(size): + """ + Generate counting series of 32-bit hex values with 8-bit increments. + Each 32-bit word contains 4 consecutive 8-bit values in little-endian format. + Example: 03020100, 07060504, 0B0A0908, 0F0E0D0C, ... + """ + result = [] + for i in range(size): + # Each word contains 4 consecutive bytes + base = i * 4 + byte0 = (base + 0) & 0xFF + byte1 = (base + 1) & 0xFF + byte2 = (base + 2) & 0xFF + byte3 = (base + 3) & 0xFF + + # Pack in little-endian format: byte3|byte2|byte1|byte0 + word = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0 + result.append(f"{word:08X}") + + return result + +def generate_addresses_2d(start, d0_stride, d0_length, d1_stride, d1_length, transactions, N): """ Generate addresses ensuring total transactions match, processing N words per transaction. Strides are already adjusted with `>> elem_offset_bit` to align with word-based addressing. @@ -22,34 +43,138 @@ def generate_addresses(start, d0_stride, d0_length, d1_stride, d1_length, transa addr = start count = 0 - print(f"Generating addresses starting from {hex(start)} with N={N} words per transaction") + print(f"Generating addresses (2D) starting from {hex(4*start)} [Byte Address] with N={N} words per transaction") print(f"d0_stride: {d0_stride}, d0_length: {d0_length}, d1_stride: {d1_stride}, d1_length: {d1_length}") for d1 in range(d1_length): addr_d1 = addr + d1 * d1_stride - print(f"Generating addresses for d1={d1} at {hex(addr_d1)}") + print(f"Generating addresses for d1={d1} at {hex(4*addr_d1)} [Byte Address]") for d0 in range(d0_length): addr_d0 = addr_d1 + d0 * d0_stride # ToDo(cdurrer): shouldnt this be addr_d1* + d0 * d0_stride? if addr_d0 + N <= MEMORY_SIZE: # Ensure full block fits block = [addr_d0 + i for i in range(N)] - print(f"Appending address block: {', '.join(hex(a) for a in block)}") + print(f" Appending address block: {', '.join(hex(4*a) for a in block)} [Byte Addresses]") addresses.append([addr_d0 + i for i in range(N)]) # Read/Write N words count += 1 else: - print(f"Warning: Address block starting at {hex(addr_d0)} exceeds memory size. Skipping.") + print(f" Warning: Address block starting at {hex(4*addr_d0)} exceeds memory size. Skipping. [Byte Address]") if count == transactions: # Stop when enough transactions are generated print(f"Transaction limit reached! Generated total of {count} transactions.") return addresses return addresses +def generate_addresses_3d(start, d0_stride, d0_length, d1_stride, d1_length, d2_stride, d2_length, transactions, N): + """ + Generate addresses ensuring total transactions match, processing N words per transaction. + Strides are adjusted with `>> 2` to align with word-based addressing. + """ + addresses = [] + addr = start + count = 0 + + print(f"Generating addresses (3D) starting from {hex(start)} with N={N} words per transaction") + print(f"d0_stride: {d0_stride}, d0_length: {d0_length}, d1_stride: {d1_stride}, d1_length: {d1_length}, d2_stride: {d2_stride}, d2_length: {d2_length}") + + for d2 in range(d2_length): + addr_d2 = addr + d2 * d2_stride + # print(f"Generating addresses for d2={d2} at {hex(addr_d2)}") + print(f"Generating addresses for d2={d2} at {hex(4*addr_d2)} [Byte Address]") + for d1 in range(d1_length): + addr_d1 = addr_d2 + d1 * d1_stride + # print(f"Generating addresses for d1={d1} at {hex(addr_d1)}") + print(f" Generating addresses for d1={d1} at {hex(4*addr_d1)} [Byte Address]") + for d0 in range(d0_length): + addr_d0 = addr_d1 + d0 * d0_stride # ToDo(cdurrer): shouldnt this be addr_d1* + d0 * d0_stride? + if addr_d0 + N <= MEMORY_SIZE: # Ensure full block fits + block = [addr_d0 + i for i in range(N)] + # print(f"Appending address block: {', '.join(hex(a) for a in block)}") + print(f" Appending address block: {', '.join(hex(4*a) for a in block)} [Byte Addresses]") + addresses.append([addr_d0 + i for i in range(N)]) # Read/Write N words + count += 1 + else: + # print(f"Warning: Address block starting at {hex(addr_d0)} exceeds memory size. Skipping.") + print(f" Warning: Address block starting at {hex(4*addr_d0)} exceeds memory size. Skipping. [Byte Address]") + if count == transactions: # Stop when enough transactions are generated + print(f"Transaction limit reached! Generated total of {count} transactions.") + return addresses + + return addresses + +def generate_addresses_transpose_read(read_start, d0_length, d1_length, transp_len, transactions, transp_mode, N): + """ + Generate addresses for transposed access patterns. + Strides are already adjusted with `>> elem_offset_bit` to align with word-based addressing. + """ + read_addresses = [] + count = 0 + + print(f"Generating read addresses (Transpose Mode {transp_mode})") + print(f"d0_length: {d0_length}, d1_length: {d1_length}, N={N} words per transaction") + + for d1 in range(d1_length): + # addr_d1 = addr + d1 * d0_length*transp_mode # In transpose, d1 stride is d0_length + # print(f"Generating addresses for d1={d1} at {hex(4*addr_d1)} [Byte Address]") + for d0 in range(d0_length): + # addr_d0 = addr_d1 + d0 # In transpose, d0 stride is 1 + read_addr = read_start + d1*(d0_length*N) + d0 + if (read_addr + N <= MEMORY_SIZE): # Ensure full block fits + read_block = [read_addr + i for i in range(N)] + read_addresses.append(read_block) + + # Debug prints showing the actual appended address blocks + read_byte_addrs = [hex(4*addr) for addr in read_block] + print(f" Appending read address block: {', '.join(read_byte_addrs)} [Byte Addresses]") + + count += 1 + else: + print(f" Warning: Read/Write address block starting at {hex(4*read_addr)} exceeds memory size. Skipping. [Byte Address]") + if count == transactions: # Stop when enough transactions are generated + print(f"Transaction limit reached! Generated total of {count} transactions.") + return read_addresses + return read_addresses + +def generate_addresses_transpose_write(write_start, d0_length, d1_length, transp_len, transactions, transp_mode, N): + """ + Generate addresses for transposed access patterns. + Strides are already adjusted with `>> elem_offset_bit` to align with word-based addressing. + """ + write_addresses = [] + count = 0 + + print(f"Generating write addresses (Transpose Mode {transp_mode})") + print(f"d0_length: {d0_length}, d1_length: {d1_length}, N={N} words per transaction") + + for d1 in range(d1_length): + # addr_d1 = addr + d1 * d0_length*transp_mode # In transpose, d1 stride is d0_length + # print(f"Generating addresses for d1={d1} at {hex(4*addr_d1)} [Byte Address]") + for d0 in range(d0_length): + # addr_d0 = addr_d1 + d0 # In transpose, d0 stride is 1 + write_addr = write_start + d0*(d1_length*N) + (d1*N) + if (write_addr + N <= MEMORY_SIZE): # Ensure full block fits + write_block = [write_addr + i for i in range(N)] + write_addresses.append(write_block) + + # Debug prints showing the actual appended address blocks + write_byte_addrs = [hex(4*addr) for addr in write_block] + print(f" Appending write address block: {', '.join(write_byte_addrs)} [Byte Addresses]") + + count += 1 + else: + print(f" Warning: Read/Write address block starting at {hex(4*write_addr)} exceeds memory size. Skipping. [Byte Address]") + if count == transactions: # Stop when enough transactions are generated + print(f"Transaction limit reached! Generated total of {count} transactions.") + return write_addresses + return write_addresses + + def update_memory(memory, write_addresses, extracted_data): """Update memory with extracted data using generated write addresses, processing N words at a time.""" for addr_block, data_block in zip(write_addresses, extracted_data): for addr, value in zip(addr_block, data_block): if addr < len(memory): memory[addr] = value # Write N words at a time - print(f"Writing value {value} to address {hex(addr)}") + print(f"Writing value {value} to address {hex(4*addr)}") def write_file(output_dir, filename, content): """Write list content to a file.""" @@ -76,6 +201,7 @@ def main(): parser.add_argument("--num_elem_word", type=int, default=4, help="Number of elements in a memory bank word") parser.add_argument("--elem_width", type=int, default=8, help="Width of each element (in bits)") parser.add_argument("--transp_mode", type=int, default=0, help="Transposition mode (3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem)") + parser.add_argument("--transp_len", type=int, default=0, help="Transposition length") parser.add_argument("--output_dir", type=str, default="output", help="Directory for storing output files") args = parser.parse_args() @@ -92,16 +218,17 @@ def main(): if args.bandwidth_bits % WORD_SIZE_BITS != 0: raise ValueError("bandwidth_bits must be a multiple of the word size (num_elem_word * elem_width).") # no transposition currently supported - if args.transp_mode != 0: - raise NotImplementedError("Transposition modes other than 'none' are not currently supported.") + # if args.transp_mode != 0: + # raise NotImplementedError("Transposition modes other than 'none' are not currently supported.") bandwidth_N = args.bandwidth_bits // WORD_SIZE_BITS print(f"Memory Size: {MEMORY_SIZE} entries") # Step 1: Generate initial memory - # memory = generate_random_hex_32bit(MEMORY_SIZE) - memory = generate_counting_hex_32bit(MEMORY_SIZE) # For debugging purposes + # memory = generate_random_hex_32bit(MEMORY_SIZE) # For testing + # memory = generate_counting_hex_32bit(MEMORY_SIZE) # For debugging + memory = generate_counting_hex_8bit(MEMORY_SIZE) # For debugging with 8-bit elements # Convert addresses from element-addressing (e.g., byte-addressing) to word-addressing elem_offset_bit = (args.num_elem_word).bit_length() - 1 @@ -113,32 +240,47 @@ def main(): args.read_d0_stride = args.read_d0_stride >> elem_offset_bit args.read_d1_stride = args.read_d1_stride >> elem_offset_bit - # Step 2: Generate read addresses (Word-aligned) - read_transactions = args.read_d0_length * args.read_d1_length - read_addresses = generate_addresses( - args.read_base_addr, args.read_d0_stride, args.read_d0_length, - args.read_d1_stride, args.read_d1_length, read_transactions, bandwidth_N - ) - - # Step 3: Extract memory values based on read addresses - extracted_data = [[memory[addr] for addr in block] for block in read_addresses if all(addr < MEMORY_SIZE for addr in block)] - - # Step 4: Save initial memory - write_file(args.output_dir, "initial_memory.txt", memory) - - # Step 5: Save debug info (addresses read and values extracted) - debug_info = [ - f"Read Block: {', '.join(hex(addr) for addr in block)} -> Data: {', '.join(memory[addr] for addr in block)}" - for block in read_addresses if all(addr < MEMORY_SIZE for addr in block) - ] - write_file(args.output_dir, "debug_values.txt", debug_info) - - # Step 6: Generate write addresses (Word-aligned) - write_transactions = read_transactions - write_addresses = generate_addresses( - args.write_base_addr, args.write_d0_stride, args.write_d0_length, - args.write_d1_stride, args.write_d1_length, write_transactions, bandwidth_N - ) + if (args.transp_mode == 0): + + # Step 2: Generate read addresses (Word-aligned) + read_transactions = args.read_d0_length * args.read_d1_length + read_addresses = generate_addresses_2d( + args.read_base_addr, args.read_d0_stride, args.read_d0_length, + args.read_d1_stride, args.read_d1_length, read_transactions, bandwidth_N + ) + + # Step 3: Extract memory values based on read addresses + extracted_data = [[memory[addr] for addr in block] for block in read_addresses if all(addr < MEMORY_SIZE for addr in block)] + + # Step 4: Save initial memory + write_file(args.output_dir, "initial_memory.txt", memory) + + # Step 5: Save debug info (addresses read and values extracted) + debug_info = [ + f"Read Block: {', '.join(hex(addr) for addr in block)} -> Data: {', '.join(memory[addr] for addr in block)}" + for block in read_addresses if all(addr < MEMORY_SIZE for addr in block) + ] + write_file(args.output_dir, "debug_values.txt", debug_info) + + # Step 6: Generate write addresses (Word-aligned) + write_transactions = read_transactions + write_addresses = generate_addresses_2d(args.write_base_addr, args.write_d0_stride, args.write_d0_length, args.write_d1_stride, args.write_d1_length, write_transactions, bandwidth_N) + + else: # Transpose mode + print(f"Transposition mode {args.transp_mode} selected.") + if (args.transp_len == 0): + args.transp_len = args.bandwidth_bits // args.elem_width + + # Step 2: Generate read addresses (Word-aligned) + read_transactions = args.read_d0_length * args.read_d1_length + read_addresses = generate_addresses_transpose_read(args.read_base_addr, args.read_d0_length, args.read_d1_length, args.transp_len, read_transactions, args.transp_mode, bandwidth_N) + write_addresses = generate_addresses_transpose_write(args.write_base_addr, args.write_d0_length, args.write_d1_length, args.transp_len, read_transactions, args.transp_mode, bandwidth_N) + + # Step 3: Extract memory values based on read addresses + extracted_data = [[memory[addr] for addr in block] for block in read_addresses if all(addr < MEMORY_SIZE for addr in block)] + + # Step 4: Save initial memory + write_file(args.output_dir, "initial_memory.txt", memory) # Step 7: Update memory with extracted data at write addresses update_memory(memory, write_addresses, extracted_data) diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py new file mode 100644 index 0000000..400cff9 --- /dev/null +++ b/verif/python/generate_stimuli_test.py @@ -0,0 +1,198 @@ +import random +import argparse +import os + +# OUTPUT_DIR = "generated" + +# BANDWIDTH = 128 # in bits +# WORD_WIDTH = 32 # in bits +# ELEM_WIDTH = 8 # in bits +# MEMORY_SIZE = 512 # in words + +# TRANSP_MODE = 4 # 0 = none, 1 = 1 elem, 2 = 2 elem, 4 = 4 elem + +# NUM_ELEM_WORD = WORD_WIDTH // ELEM_WIDTH # e.g., 4 for 32-bit words with 8-bit elements +# BANDWIDTH_ELEMS = BANDWIDTH // ELEM_WIDTH +# BANDWIDTH_WORDS = BANDWIDTH_ELEMS // NUM_ELEM_WORD + +# MATRIX_SIZE_D0 = 16 # in elements +# MATRIX_SIZE_D1 = 4 # in elements + +# READ_BASE_ADDR = 0 # in bytes +# READ_D0_LENGTH = MATRIX_SIZE_D0 // BANDWIDTH_ELEMS # Nof accesses with bandwidth BW per D0-transfer ("row") +# READ_D1_LENGTH = MATRIX_SIZE_D1 + +# WRITE_BASE_ADDR = 128 # in bytes +# WRITE_D0_LENGTH = READ_D1_LENGTH +# WRITE_D1_LENGTH = READ_D0_LENGTH + +def extract_elements_from_word(word, word_width, elem_width): + """Extract elements from a word based on the specified widths.""" + word_int = int(word, 16) # Convert hex string to integer + + elements = [] + for i in range(word_width // elem_width): + # Extract each element using shift and mask + elem_val = (word_int >> (i * elem_width)) & ((1 << elem_width) - 1) + elements.append(elem_val) + return elements + +def generate_counting_hex(size, elem_width, word_width): + """ + Generate counting series of hex values with specified element and word widths. + Each word contains multiple elements in little-endian format. + """ + elems_per_word = word_width // elem_width + result = [] + for i in range(size): + word_val = 0 + for j in range(elems_per_word): + elem_val = (i * elems_per_word + j) & ((1 << elem_width) - 1) + word_val |= (elem_val << (j * elem_width)) + result.append(f"{word_val:0{word_width // 4}X}") # Format as hex string + + return result + +def pack_elements_to_word(elements, elem_width, word_width): + """Pack multiple elements into a single word.""" + elems_per_word = word_width // elem_width + word_val = 0 + + for i, elem in enumerate(elements[:elems_per_word]): # Take only what fits in a word + word_val |= (elem << (i * elem_width)) + + return f"{word_val:0{word_width // 4}X}" + +def matrix_to_hex_words(matrix, elem_width, word_width): + """Convert a matrix of elements to a list of hex words.""" + elems_per_word = word_width // elem_width + hex_words = [] + + for row in matrix: + # Process each row, grouping elements into words + for i in range(0, len(row), elems_per_word): + elements_for_word = row[i:i + elems_per_word] + + # Pad with zeros if the row doesn't fill a complete word + while len(elements_for_word) < elems_per_word: + elements_for_word.append(0) + + # Pack elements into a word + hex_word = pack_elements_to_word(elements_for_word, elem_width, word_width) + hex_words.append(hex_word) + + return hex_words + +def write_file(output_dir, filename, content): + """Write list content to a file.""" + os.makedirs(output_dir, exist_ok=True) # Ensure directory exists + filepath = os.path.join(output_dir, filename) + with open(filepath, "w") as file: + file.write("\n".join(content) + "\n") + +def transpose(matrix, size_d0, size_d1, transp_mode): + transposed = [[0 for _ in range(size_d1 * transp_mode)] for _ in range(size_d0 // transp_mode)] + for d1 in range(size_d1): + for d0 in range(size_d0 // transp_mode): + for i in range(transp_mode): + transposed[d0][(d1*transp_mode)+i] = matrix[d1][(d0*transp_mode)+i] + return transposed + +def main(): + # Parse command-line arguments + parser = argparse.ArgumentParser(description="Memory Read/Write Simulation with Word-Aligned Strides") + parser.add_argument("--mem_size", type=int, default=0x30, help="Memory size in entries") + parser.add_argument("--read_base_addr", type=int, default=0x00, help="Base address for read operations") + parser.add_argument("--write_base_addr", type=int, default=0x20, help="Base address for write operations") + parser.add_argument("--read_d0_stride", type=int, default=4, help="Stride for d0 read (in bytes)") + parser.add_argument("--read_d1_stride", type=int, default=16, help="Stride for d1 read (in bytes)") + parser.add_argument("--read_d0_length", type=int, default=4, help="Length for d0 read") + parser.add_argument("--read_d1_length", type=int, default=4, help="Length for d1 read") + parser.add_argument("--write_d0_stride", type=int, default=4, help="Stride for d0 write (in bytes)") + parser.add_argument("--write_d1_stride", type=int, default=16, help="Stride for d1 write (in bytes)") + parser.add_argument("--write_d0_length", type=int, default=4, help="Length for d0 write") + parser.add_argument("--write_d1_length", type=int, default=4, help="Length for d1 write") + parser.add_argument("--bandwidth_bits", type=int, default=4, help="Number of bits per transaction") + parser.add_argument("--num_elem_word", type=int, default=4, help="Number of elements in a memory bank word") + parser.add_argument("--elem_width", type=int, default=8, help="Width of each element (in bits)") + parser.add_argument("--transp_mode", type=int, default=0, help="Transposition mode (3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem)") + parser.add_argument("--transp_len", type=int, default=0, help="Transposition length") + parser.add_argument("--output_dir", type=str, default="output", help="Directory for storing output files") + + args = parser.parse_args() + + MEMORY_SIZE = args.mem_size # Set global memory size + BANDWIDTH_ELEMS = args.bandwidth_bits // args.elem_width + BANDWIDTH_WORDS = BANDWIDTH_ELEMS // args.num_elem_word + WORD_SIZE_BITS = args.num_elem_word * args.elem_width # Set global word size in bits + + ELEM_WIDTH = args.elem_width + WORD_WIDTH = args.num_elem_word * args.elem_width + NUM_ELEM_WORD = args.num_elem_word + READ_BASE_ADDR = args.read_base_addr + READ_D0_LENGTH = args.read_d0_length + READ_D1_LENGTH = args.read_d1_length + WRITE_BASE_ADDR = args.write_base_addr + TRANSP_MODE = args.transp_mode + MATRIX_SIZE_D0 = READ_D0_LENGTH * BANDWIDTH_ELEMS + MATRIX_SIZE_D1 = READ_D1_LENGTH + OUTPUT_DIR = args.output_dir + + # num_elem_word must be power of two and greater than zero + if args.num_elem_word & (args.num_elem_word - 1) != 0 or args.num_elem_word <= 0: + raise ValueError("num_elem_word must be a power of two and greater than zero.") + # bandwidth width must be a multiple of word size + if args.bandwidth_bits % WORD_SIZE_BITS != 0: + raise ValueError("bandwidth_bits must be a multiple of the word size (num_elem_word * elem_width).") + + + print(f"Memory Size: {MEMORY_SIZE} entries") + print(f"Word Size: {WORD_SIZE_BITS} bits") + + memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) # For debugging with 8-bit elements + + write_file(OUTPUT_DIR, "initial_memory.txt", memory) + + # Extract matrix (read dimensions) from memory + input_matrix = [] + for d1 in range(READ_D1_LENGTH): + row = [] + for d0 in range(READ_D0_LENGTH*BANDWIDTH_WORDS): + word = memory[(READ_BASE_ADDR // (WORD_WIDTH // 8) + d1 * (READ_D0_LENGTH*BANDWIDTH_WORDS) + d0)] + word_elements = extract_elements_from_word(word, NUM_ELEM_WORD*ELEM_WIDTH, ELEM_WIDTH) + for elem in range(NUM_ELEM_WORD): + row.append(word_elements[elem]) + input_matrix.append(row) + + # Print input matrix + print("Input Matrix:") + for i, row in enumerate(input_matrix): + print(f"Row {i}: {row}") + + # for d0 in range(d0_length): + # for d1 in range(d1_length): + # input_matrix[d0][d1] = d0*d1_length + d1 + # print("Input Matrix:") + # for row in input_matrix: + # print(row) + + transposed_matrix = transpose(input_matrix, MATRIX_SIZE_D0, MATRIX_SIZE_D1, TRANSP_MODE) + print("\nTransposed Matrix:") + for i, row in enumerate(transposed_matrix): + print(f"Row {i}: {row}") + + # Convert transposed matrix back to words + transposed_hex_words = matrix_to_hex_words(transposed_matrix, ELEM_WIDTH, WORD_WIDTH) + # print(f"\nTransposed Matrix as Hex Words:") + # for i, word in enumerate(transposed_hex_words): + # print(f"Word {i}: {word}") + + # Write back transposed matrix to memory at WRITE_BASE_ADDR + for i, word in enumerate(transposed_hex_words): + memory[(WRITE_BASE_ADDR // (WORD_WIDTH // 8) + i)] = word + # print(f"Writing word {word} to memory address {WRITE_BASE_ADDR + i}") + + write_file(OUTPUT_DIR, "updated_memory.txt", memory) + +if __name__ == "__main__": + main() diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index 30c03f5..fc8bb45 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -61,7 +61,8 @@ import tb_package::*; logic periph_r_valid; logic [PERIPH_ID-1:0] periph_r_id; - + logic [2:0] transp_mode; + logic [15:0] transp_len; // Performs one entire clock cycle. task cycle; @@ -116,6 +117,9 @@ import tb_package::*; // assign read_addr = '{`STIM_READ_BASE_ADDR, 32'h4, 32'h10, 32'h4, 32'h4, 32'h10}; // assign write_addr = '{32'h40, 32'h4, 32'h10, 32'h4, 32'h4, 32'h10}; + assign transp_mode = `STIM_TRANSP_MODE; + assign transp_len = `STIM_TRANSP_LEN; + datamover_top_wrap #( // waive all asserts in testbench at this stage: the dummy memory @@ -220,6 +224,7 @@ import tb_package::*; initial begin : main_execution logic [31:0] len0_reg; logic [31:0] len1_reg; + logic [31:0] transp_mode_reg; $info("Start execution...\n"); @@ -240,17 +245,18 @@ import tb_package::*; $info("[%0t] Acquiring job...\n", $time); // acquire job $info("Acquiring job...\n"); - while(status !== 32'h00) - periph_read(datamover_package::DATAMOVER_ACQUIRE, datamover_package::HWPE_REGISTER_OFFS, status, clk_i, periph_bus); + while(status != 32'h00) + periph_read(datamover_package::DATAMOVER_ACQUIRE, datamover_package::HWPE_REGISTER_OFFS, status, clk_i, periph_bus); $info("Job acquired, configuring datamover...\n"); - + periph_write(datamover_package::DATAMOVER_REG_IN_PTR, datamover_package::DATAMOVER_REGISTER_OFFS, read_addr.base_addr, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_OUT_PTR, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.base_addr, clk_i, periph_bus); - + // Configure packed length registers (see datamover_package.sv) len0_reg = {read_addr.d1_length[7:0], read_addr.d0_length[11:0], read_addr.tot_length[11:0]}; len1_reg = {4'b0, read_addr.d1_length[11:8], write_addr.d1_length[11:0], write_addr.d0_length[11:0]}; + transp_mode_reg = {transp_len, 13'b0, transp_mode}; // ToDo(cdurrer): Leftover = transp_len??? // Make sure tot_length is the same for read and write assert (read_addr.tot_length == write_addr.tot_length) else $fatal("Read and write total lengths do not match!"); @@ -266,16 +272,16 @@ import tb_package::*; periph_write(datamover_package::DATAMOVER_REG_OUT_D2_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, 32'h4, clk_i, periph_bus); // Transposition mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) - periph_write(datamover_package::DATAMOVER_REG_TRANSP_MODE, datamover_package::DATAMOVER_REGISTER_OFFS, {29'b0, TRANSP_MODE}, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_TRANSP_MODE, datamover_package::DATAMOVER_REGISTER_OFFS, transp_mode_reg, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_COMMIT_AND_TRIGGER, datamover_package::HWPE_REGISTER_OFFS, 32'h0, clk_i, periph_bus); - while(status === 32'h00) + while(status == 32'h00) periph_read(datamover_package::DATAMOVER_STATUS, datamover_package::HWPE_REGISTER_OFFS, status, clk_i, periph_bus); $info("Datamover working...\n"); - - while(status !== 32'h00) + + while(status != 32'h00) periph_read(datamover_package::DATAMOVER_STATUS, datamover_package::HWPE_REGISTER_OFFS, status, clk_i, periph_bus); $info("Datamover finished transfer. Checking output...\n"); From c8ba826eaee9196c57950a9eff5753652da84b2f Mon Sep 17 00:00:00 2001 From: cdurrer Date: Tue, 4 Nov 2025 16:23:50 +0100 Subject: [PATCH 10/29] bender: bump hwpe-ctrl version --- Bender.lock | 2 +- Bender.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Bender.lock b/Bender.lock index b198637..224983c 100644 --- a/Bender.lock +++ b/Bender.lock @@ -49,7 +49,7 @@ packages: - redundancy_cells - register_interface hwpe-ctrl: - revision: 4977b6cf42b96f3fefd1973281d5c4b8259c50d5 + revision: d63ee3ec17af9b0cce0640fa88475bfeb0c5c93d version: null source: Git: https://github.com/pulp-platform/hwpe-ctrl.git diff --git a/Bender.yml b/Bender.yml index 8a904bc..f471625 100644 --- a/Bender.yml +++ b/Bender.yml @@ -8,7 +8,7 @@ package: dependencies: hwpe-stream: { git: "https://github.com/pulp-platform/hwpe-stream.git", rev: 40ab0fe1433dc080a49aa1926ce09df9ab3f5fb5 } # branch: prasadar/multi-precision hci: { git: "https://github.com/pulp-platform/hci.git", rev: 9844a893c34612ac66697e8dcd60214296f2634c } # branch: prasadar/multi-precision - hwpe-ctrl: { git: "https://github.com/pulp-platform/hwpe-ctrl.git", rev: 4977b6cf42b96f3fefd1973281d5c4b8259c50d5 } # branch: master + hwpe-ctrl: { git: "https://github.com/pulp-platform/hwpe-ctrl.git", rev: d63ee3ec17af9b0cce0640fa88475bfeb0c5c93d } # version: 2.0 } sources: From 835a8b4cc077728192892a6a872453ef49efe6ca Mon Sep 17 00:00:00 2001 From: cdurrer Date: Tue, 4 Nov 2025 16:26:51 +0100 Subject: [PATCH 11/29] Fix configuration definitions for matrix mode and updated golden model --- config.mk | 62 +++++--- verif/python/generate_stimuli_konark.py | 193 ------------------------ verif/python/generate_stimuli_test.py | 33 ++-- 3 files changed, 64 insertions(+), 224 deletions(-) delete mode 100644 verif/python/generate_stimuli_konark.py diff --git a/config.mk b/config.mk index e0060f1..acd48fc 100644 --- a/config.mk +++ b/config.mk @@ -9,12 +9,12 @@ # Datamover hw config # ####################### -BANDWIDTH = 128 # in bits +BANDWIDTH = 32 # in bits WORD_WIDTH = 32 # in bits ELEM_WIDTH = 8 # in bits -MEMORY_SIZE = 512 # in words +MEMORY_SIZE = 1024 # in words -TRANSP_MODE = 4 # 0 = none, 1 = 1 elem, 2 = 2 elem, 4 = 4 elem +TRANSP_MODE = 1 # 0 = none, 1 = 1 elem, 2 = 2 elem, 4 = 4 elem # Derived constants from basic parameters # BANDWIDTH_WORDS := $(shell echo $$(($(BANDWIDTH) / $(WORD_WIDTH)))) # Number of words per bandwidth @@ -26,27 +26,29 @@ NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of ######################### # Base matrix dimensions (in elements) -MATRIX_SIZE_D0 ?= 16 # Matrix width in elements -MATRIX_SIZE_D1 ?= 8 # Matrix height in elements +MATRIX_SIZE_N ?= 8 # Matrix width in elements +MATRIX_SIZE_M ?= 8 # Matrix height in elements # Derived stride calculations # ELEM_STRIDE_D0 := $(ELEM_WIDTH) # Element-to-element stride in bits -# ROW_STRIDE_BYTES := $(shell echo $$(($(MATRIX_SIZE_D0) * $(ELEM_WIDTH) / 8))) # Bytes per row +# ROW_STRIDE_BYTES := $(shell echo $$(($(MATRIX_SIZE_N) * $(ELEM_WIDTH) / 8))) # Bytes per row # ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (4*32b) # N (bandwidth) consecutive words are read/written in one transaction STIM_READ_BASE_ADDR ?= 0 # Element-addressed -STIM_READ_D0_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_D0) / $(BANDWIDTH_ELEMS)))) # Nof accesses with bandwidth BW per D0-transfer ("row") -STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # Elements -STIM_READ_D1_LENGTH ?= $(MATRIX_SIZE_D1) # Number of full D0-transfers ("rows") -STIM_READ_D1_STRIDE ?= $(MATRIX_SIZE_D0) # Elements -> manually compute "next row" stride +STIM_READ_D0_LENGTH ?= $(MATRIX_SIZE_M) # $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # Nof accesses with bandwidth BW per D0-transfer ("row") +STIM_READ_D0_STRIDE ?= $(MATRIX_SIZE_N) # $(BANDWIDTH_ELEMS) # Elements +STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # $(MATRIX_SIZE_M) # Number of full D0-transfers ("rows") +STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # $(MATRIX_SIZE_N) # Elements -> manually compute "next row" stride STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # Total memory accesses -STIM_WRITE_BASE_ADDR ?= 128 # Element-addressed -STIM_WRITE_D0_LENGTH ?= $(STIM_READ_D1_LENGTH) # Transpose: read height becomes write width -STIM_WRITE_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # Bytes per transposed row -STIM_WRITE_D1_LENGTH ?= $(STIM_READ_D0_LENGTH) # Transpose: read width becomes write height -STIM_WRITE_D1_STRIDE ?= $(MATRIX_SIZE_D0) # Elements +STIM_WRITE_BASE_ADDR ?= 1024 # Element-addressed +TEMP_MULT1 := $(shell echo $$(($(MATRIX_SIZE_M) * $(TRANSP_MODE)))) # $(shell echo $$(($(STIM_READ_D1_LENGTH) * $(TRANSP_MODE)))) +STIM_WRITE_D0_LENGTH ?= $(MATRIX_SIZE_N) # 8 # $(shell echo $$(($(TEMP_MULT1) / $(BANDWIDTH_ELEMS)))) # Transpose: read height becomes write width +STIM_WRITE_D0_STRIDE ?= $(MATRIX_SIZE_M) # 16 #$(BANDWIDTH_ELEMS) # Bytes per transposed row ToDo(cdurrer): elements or bytes? +# TEMP_MULT2 := # $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(BANDWIDTH_ELEMS)))) +STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_M) / $(BANDWIDTH_ELEMS)))) # 4 # $(shell echo $$(($(MATRIX_SIZE_N) / $(TRANSP_MODE)))) # $(shell echo $$(($(TEMP_MULT2) / $(TRANSP_MODE)))) # Transpose: read width becomes write height +STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # $(shell echo $$(($(STIM_WRITE_D0_LENGTH) * $(BANDWIDTH_ELEMS)))) # Elements STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read STIM_MEM_SIZE ?= $(MEMORY_SIZE) #65536 @@ -55,8 +57,28 @@ STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 3'b000 = none, 3'b001 = 1 elem, 3' STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH # Debug: Print computed values (uncomment to see values during make) -$(info WORD_WIDTH=$(WORD_WIDTH)) -$(info BANDWIDTH_ELEMS=$(BANDWIDTH_ELEMS)) -$(info ROW_STRIDE_BYTES=$(ROW_STRIDE_BYTES)) -$(info STIM_READ_D0_LENGTH=$(STIM_READ_D0_LENGTH)) -$(info STIM_WRITE_D0_STRIDE=$(STIM_WRITE_D0_STRIDE)) +$(info BANDWIDTH_ELEMS: $(BANDWIDTH_ELEMS)) +$(info WORD_WIDTH: $(WORD_WIDTH)) +$(info ELEM_WIDTH: $(ELEM_WIDTH)) + +$(info BANDWIDTH_ELEMS: $(BANDWIDTH_ELEMS)) +$(info NUM_ELEM_WORD: $(NUM_ELEM_WORD)) + +$(info MATRIX_SIZE_N: $(MATRIX_SIZE_N)) +$(info MATRIX_SIZE_M: $(MATRIX_SIZE_M)) + +$(info STIM_READ_BASE_ADDR: $(STIM_READ_BASE_ADDR)) +$(info STIM_READ_D0_LENGTH: $(STIM_READ_D0_LENGTH)) +$(info STIM_WRITE_D0_STRIDE: $(STIM_WRITE_D0_STRIDE)) +$(info STIM_READ_D1_LENGTH: $(STIM_READ_D1_LENGTH)) +$(info STIM_READ_D1_STRIDE: $(STIM_READ_D1_STRIDE)) +$(info STIM_READ_TOT_LENGTH: $(STIM_READ_TOT_LENGTH)) + +$(info STIM_WRITE_BASE_ADDR: $(STIM_WRITE_BASE_ADDR)) +$(info STIM_WRITE_D0_LENGTH: $(STIM_WRITE_D0_LENGTH)) +$(info STIM_WRITE_D0_STRIDE: $(STIM_WRITE_D0_STRIDE)) +$(info STIM_WRITE_D1_LENGTH: $(STIM_WRITE_D1_LENGTH)) +$(info STIM_WRITE_D1_STRIDE: $(STIM_WRITE_D1_STRIDE)) +$(info STIM_WRITE_TOT_LENGTH: $(STIM_WRITE_TOT_LENGTH)) + +$(info STIM_TRANSP_MODE: $(STIM_TRANSP_MODE)) diff --git a/verif/python/generate_stimuli_konark.py b/verif/python/generate_stimuli_konark.py deleted file mode 100644 index dafce2a..0000000 --- a/verif/python/generate_stimuli_konark.py +++ /dev/null @@ -1,193 +0,0 @@ -import random -import argparse -import os - -def generate_random_hex_32bit(size): - """Generate random 32-bit hex values.""" - return [f"{random.randint(0, 2**32 - 1):08X}" for _ in range(size)] - -def generate_counting_words_hex_32bit(size): - """Generate counting series of 32-bit hex values (0, 1, 2, 3, ...).""" - return [f"{i:08X}" for i in range(size)] - -def generate_counting_bytes_hex_32bit(size): - """Generate counting series of 32-bit hex values (0, 1, 2, 3, ...).""" - return [f"{(i*4):08X}" for i in range(size)] - -# def generate_counting_bytes_hex_32bit(size): -# """Generate counting series packed as 32-bit hex values (byte-wise counting in little-endian format). -# Example: 03020100, 07060504, 0B0A0908, ... -# """ -# result = [] -# for i in range(size): -# # Pack 4 consecutive bytes into a 32-bit word (little-endian) -# byte0 = (i * 4 + 0) & 0xFF -# byte1 = (i * 4 + 1) & 0xFF -# byte2 = (i * 4 + 2) & 0xFF -# byte3 = (i * 4 + 3) & 0xFF -# # Pack as little-endian: [byte3][byte2][byte1][byte0] -# word = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0 -# result.append(f"{word:08X}") -# return result - -def generate_addresses_2d(start, d0_stride, d0_length, d1_stride, d1_length, transactions, N): - """ - Generate addresses ensuring total transactions match, processing N words per transaction. - Strides are adjusted with `>> 2` to align with word-based addressing. - """ - addresses = [] - addr = start - count = 0 - - print(f"Generating addresses (2D) starting from {hex(start)} with N={N} words per transaction") - print(f"d0_stride: {d0_stride}, d0_length: {d0_length}, d1_stride: {d1_stride}, d1_length: {d1_length}") - - for d1 in range(d1_length): - addr_d1 = addr + d1 * d1_stride - print(f"Generating addresses for d1={d1} at {hex(addr_d1)}") - for d0 in range(d0_length): - addr_d0 = addr_d1 + d0 * d0_stride # ToDo(cdurrer): shouldnt this be addr_d1* + d0 * d0_stride? - if addr_d0 + N <= MEMORY_SIZE: # Ensure full block fits - block = [addr_d0 + i for i in range(N)] - print(f"Appending address block: {', '.join(hex(a) for a in block)}") - addresses.append([addr_d0 + i for i in range(N)]) # Read/Write N words - count += 1 - else: - print(f"Warning: Address block starting at {hex(addr_d0)} exceeds memory size. Skipping.") - if count == transactions: # Stop when enough transactions are generated - print(f"Transaction limit reached! Generated total of {count} transactions.") - return addresses - - return addresses - -def generate_addresses_3d(start, d0_stride, d0_length, d1_stride, d1_length, d2_stride, d2_length, transactions, N): - """ - Generate addresses ensuring total transactions match, processing N words per transaction. - Strides are adjusted with `>> 2` to align with word-based addressing. - """ - addresses = [] - addr = start - count = 0 - - print(f"Generating addresses (3D) starting from {hex(start)} with N={N} words per transaction") - print(f"d0_stride: {d0_stride}, d0_length: {d0_length}, d1_stride: {d1_stride}, d1_length: {d1_length}, d2_stride: {d2_stride}, d2_length: {d2_length}") - - for d2 in range(d2_length): - addr_d2 = addr + d2 * d2_stride - # print(f"Generating addresses for d2={d2} at {hex(addr_d2)}") - print(f"Generating addresses for d2={d2} at {hex(4*addr_d2)} [Byte Address]") - for d1 in range(d1_length): - addr_d1 = addr_d2 + d1 * d1_stride - # print(f"Generating addresses for d1={d1} at {hex(addr_d1)}") - print(f" Generating addresses for d1={d1} at {hex(4*addr_d1)} [Byte Address]") - for d0 in range(d0_length): - addr_d0 = addr_d1 + d0 * d0_stride # ToDo(cdurrer): shouldnt this be addr_d1* + d0 * d0_stride? - if addr_d0 + N <= MEMORY_SIZE: # Ensure full block fits - block = [addr_d0 + i for i in range(N)] - # print(f"Appending address block: {', '.join(hex(a) for a in block)}") - print(f" Appending address block: {', '.join(hex(4*a) for a in block)} [Byte Addresses]") - addresses.append([addr_d0 + i for i in range(N)]) # Read/Write N words - count += 1 - else: - # print(f"Warning: Address block starting at {hex(addr_d0)} exceeds memory size. Skipping.") - print(f" Warning: Address block starting at {hex(4*addr_d0)} exceeds memory size. Skipping. [Byte Address]") - if count == transactions: # Stop when enough transactions are generated - print(f"Transaction limit reached! Generated total of {count} transactions.") - return addresses - - return addresses - -def update_memory(memory, write_addresses, extracted_data): - """Update memory with extracted data using generated write addresses, processing N words at a time.""" - for addr_block, data_block in zip(write_addresses, extracted_data): - for addr, value in zip(addr_block, data_block): - if addr < len(memory): - memory[addr] = value # Write N words at a time - print(f"Writing value {value} to address {hex(addr)}") - -def write_file(output_dir, filename, content): - """Write list content to a file.""" - os.makedirs(output_dir, exist_ok=True) # Ensure directory exists - filepath = os.path.join(output_dir, filename) - with open(filepath, "w") as file: - file.write("\n".join(content) + "\n") - -def main(): - # Parse command-line arguments - parser = argparse.ArgumentParser(description="Memory Read/Write Simulation with Word-Aligned Strides") - parser.add_argument("--mem_size", type=int, default=0x30, help="Memory size in entries") - parser.add_argument("--read_base_addr", type=int, default=0x00, help="Base address for read operations") - parser.add_argument("--write_base_addr", type=int, default=0x20, help="Base address for write operations") - parser.add_argument("--read_d0_stride", type=int, default=4, help="Stride for d0 read (in bytes)") - parser.add_argument("--read_d1_stride", type=int, default=16, help="Stride for d1 read (in bytes)") - parser.add_argument("--read_d0_length", type=int, default=4, help="Length for d0 read") - parser.add_argument("--read_d1_length", type=int, default=4, help="Length for d1 read") - parser.add_argument("--write_d0_stride", type=int, default=4, help="Stride for d0 write (in bytes)") - parser.add_argument("--write_d1_stride", type=int, default=16, help="Stride for d1 write (in bytes)") - parser.add_argument("--write_d0_length", type=int, default=4, help="Length for d0 write") - parser.add_argument("--write_d1_length", type=int, default=4, help="Length for d1 write") - parser.add_argument("--bandwidth_N", type=int, default=4, help="Number of words per transaction") - parser.add_argument("--output_dir", type=str, default="output", help="Directory for storing output files") - - args = parser.parse_args() - - global MEMORY_SIZE - MEMORY_SIZE = args.mem_size # Set global memory size - - print(f"Memory Size: {MEMORY_SIZE} entries") - - # Step 1: Generate initial memory - # memory = generate_random_hex_32bit(MEMORY_SIZE) - memory = generate_counting_bytes_hex_32bit(MEMORY_SIZE) # For debugging purposes - - # Convert byte-based to word-based ( >> 2) - - args.write_base_addr = args.write_base_addr >> 2 - args.read_base_addr = args.read_base_addr >> 2 - args.write_d0_stride = args.write_d0_stride >> 2 - args.write_d1_stride = args.write_d1_stride >> 2 - args.read_d0_stride = args.read_d0_stride >> 2 - args.read_d1_stride = args.read_d1_stride >> 2 - - read_d2_length = 4 - read_d2_stride = 64 >> 2 - write_d2_length = 4 - write_d2_stride = 4352 >> 2 - - # Step 2: Generate read addresses (Word-aligned) - read_transactions = args.read_d0_length * args.read_d1_length * read_d2_length - read_addresses = generate_addresses_2d(args.read_base_addr, args.read_d0_stride, args.read_d0_length, - args.read_d1_stride, args.read_d1_length, read_transactions, args.bandwidth_N) - # read_addresses = generate_addresses_3d(args.read_base_addr, args.read_d0_stride, args.read_d0_length, - # args.read_d1_stride, args.read_d1_length, read_d2_stride, read_d2_length, read_transactions, args.bandwidth_N) - - # Step 3: Extract memory values based on read addresses - extracted_data = [[memory[addr] for addr in block] for block in read_addresses if all(addr < MEMORY_SIZE for addr in block)] - - # Step 4: Save initial memory - write_file(args.output_dir, "initial_memory.txt", memory) - - # Step 5: Save debug info (addresses read and values extracted) - debug_info = [ - f"Read Block: {', '.join(hex(addr) for addr in block)} -> Data: {', '.join(memory[addr] for addr in block)}" - for block in read_addresses if all(addr < MEMORY_SIZE for addr in block) - ] - write_file(args.output_dir, "debug_values.txt", debug_info) - - # Step 6: Generate write addresses (Word-aligned) - write_transactions = read_transactions - write_addresses = generate_addresses_2d(args.write_base_addr, args.write_d0_stride, args.write_d0_length, - args.write_d1_stride, args.write_d1_length, write_transactions, args.bandwidth_N) - # write_addresses = generate_addresses_3d(args.write_base_addr, args.write_d0_stride, args.write_d0_length, - # args.write_d1_stride, args.write_d1_length, write_d2_stride, write_d2_length, write_transactions, args.bandwidth_N) - - # Step 7: Update memory with extracted data at write addresses - update_memory(memory, write_addresses, extracted_data) - - # Step 8: Save updated memory - write_file(args.output_dir, "updated_memory.txt", memory) - - print(f"Files generated in '{args.output_dir}': initial_memory.txt, debug_values.txt, updated_memory.txt") - -if __name__ == "__main__": - main() diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py index 400cff9..72d806d 100644 --- a/verif/python/generate_stimuli_test.py +++ b/verif/python/generate_stimuli_test.py @@ -15,12 +15,12 @@ # BANDWIDTH_ELEMS = BANDWIDTH // ELEM_WIDTH # BANDWIDTH_WORDS = BANDWIDTH_ELEMS // NUM_ELEM_WORD -# MATRIX_SIZE_D0 = 16 # in elements -# MATRIX_SIZE_D1 = 4 # in elements +# MATRIX_SIZE_N = 16 # in elements +# MATRIX_SIZE_M = 4 # in elements # READ_BASE_ADDR = 0 # in bytes -# READ_D0_LENGTH = MATRIX_SIZE_D0 // BANDWIDTH_ELEMS # Nof accesses with bandwidth BW per D0-transfer ("row") -# READ_D1_LENGTH = MATRIX_SIZE_D1 +# READ_D0_LENGTH = MATRIX_SIZE_N // BANDWIDTH_ELEMS # Nof accesses with bandwidth BW per D0-transfer ("row") +# READ_D1_LENGTH = MATRIX_SIZE_M # WRITE_BASE_ADDR = 128 # in bytes # WRITE_D0_LENGTH = READ_D1_LENGTH @@ -134,8 +134,11 @@ def main(): READ_D1_LENGTH = args.read_d1_length WRITE_BASE_ADDR = args.write_base_addr TRANSP_MODE = args.transp_mode - MATRIX_SIZE_D0 = READ_D0_LENGTH * BANDWIDTH_ELEMS - MATRIX_SIZE_D1 = READ_D1_LENGTH + # MATRIX_SIZE_N = READ_D0_LENGTH * BANDWIDTH_ELEMS + # MATRIX_SIZE_M = READ_D1_LENGTH + MATRIX_SIZE_N = READ_D1_LENGTH * BANDWIDTH_ELEMS + MATRIX_SIZE_M = READ_D0_LENGTH + OUTPUT_DIR = args.output_dir # num_elem_word must be power of two and greater than zero @@ -149,16 +152,17 @@ def main(): print(f"Memory Size: {MEMORY_SIZE} entries") print(f"Word Size: {WORD_SIZE_BITS} bits") - memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) # For debugging with 8-bit elements + # memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) + memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH*TRANSP_MODE, WORD_WIDTH) write_file(OUTPUT_DIR, "initial_memory.txt", memory) # Extract matrix (read dimensions) from memory input_matrix = [] - for d1 in range(READ_D1_LENGTH): + for d1 in range(MATRIX_SIZE_M): row = [] - for d0 in range(READ_D0_LENGTH*BANDWIDTH_WORDS): - word = memory[(READ_BASE_ADDR // (WORD_WIDTH // 8) + d1 * (READ_D0_LENGTH*BANDWIDTH_WORDS) + d0)] + for d0 in range(MATRIX_SIZE_N // NUM_ELEM_WORD): + word = memory[(READ_BASE_ADDR // (WORD_WIDTH // 8) + d1 * (MATRIX_SIZE_N // NUM_ELEM_WORD) + d0)] word_elements = extract_elements_from_word(word, NUM_ELEM_WORD*ELEM_WIDTH, ELEM_WIDTH) for elem in range(NUM_ELEM_WORD): row.append(word_elements[elem]) @@ -168,6 +172,9 @@ def main(): print("Input Matrix:") for i, row in enumerate(input_matrix): print(f"Row {i}: {row}") + print("\n") + for i, row in enumerate(input_matrix): + print(f"Row {i}: {[format(elem, 'X') for elem in row]}") # for d0 in range(d0_length): # for d1 in range(d1_length): @@ -176,10 +183,13 @@ def main(): # for row in input_matrix: # print(row) - transposed_matrix = transpose(input_matrix, MATRIX_SIZE_D0, MATRIX_SIZE_D1, TRANSP_MODE) + transposed_matrix = transpose(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, TRANSP_MODE) print("\nTransposed Matrix:") for i, row in enumerate(transposed_matrix): print(f"Row {i}: {row}") + print("\n") + for i, row in enumerate(transposed_matrix): + print(f"Row {i}: {[format(elem, 'X') for elem in row]}") # Convert transposed matrix back to words transposed_hex_words = matrix_to_hex_words(transposed_matrix, ELEM_WIDTH, WORD_WIDTH) @@ -189,6 +199,7 @@ def main(): # Write back transposed matrix to memory at WRITE_BASE_ADDR for i, word in enumerate(transposed_hex_words): + print(f"Writing word {word} to memory address {WRITE_BASE_ADDR + i * (WORD_WIDTH // 8)}") memory[(WRITE_BASE_ADDR // (WORD_WIDTH // 8) + i)] = word # print(f"Writing word {word} to memory address {WRITE_BASE_ADDR + i}") From 63db310cb9dad7b47e034578eae13ce62146bb8d Mon Sep 17 00:00:00 2001 From: cdurrer Date: Wed, 5 Nov 2025 11:49:12 +0100 Subject: [PATCH 12/29] [tb, rtl] Using dimension d2 for streaming out data in transpose mode: added d2_stride and set dim_enable_1h accordingly --- Makefile | 2 ++ config.mk | 26 +++++++++++++------------- rtl/datamover_top.sv | 14 +++++++------- test/hal_datamover.h | 2 +- verif/python/generate_stimuli_test.py | 1 + verif/tb/tb_datamover_top_wrap.sv | 7 ++++--- 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/Makefile b/Makefile index 915478b..f77c6ae 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,7 @@ TESTBENCH_DEFINES += -DSTIM_WRITE_D0_STRIDE=${STIM_WRITE_D0_STRIDE} TESTBENCH_DEFINES += -DSTIM_WRITE_D0_LENGTH=${STIM_WRITE_D0_LENGTH} TESTBENCH_DEFINES += -DSTIM_WRITE_D1_STRIDE=${STIM_WRITE_D1_STRIDE} TESTBENCH_DEFINES += -DSTIM_WRITE_D1_LENGTH=${STIM_WRITE_D1_LENGTH} +TESTBENCH_DEFINES += -DSTIM_WRITE_D2_STRIDE=${STIM_WRITE_D2_STRIDE} TESTBENCH_DEFINES += -DSTIM_WRITE_TOT_LENGTH=${STIM_WRITE_TOT_LENGTH} TESTBENCH_DEFINES += -DSTIM_MEM_SIZE=${STIM_MEM_SIZE} @@ -92,6 +93,7 @@ stimuli: clean-stimuli --write_d0_length $(STIM_WRITE_D0_LENGTH) \ --write_d1_stride $(STIM_WRITE_D1_STRIDE) \ --write_d1_length $(STIM_WRITE_D1_LENGTH) \ + --write_d2_stride $(STIM_WRITE_D2_STRIDE) \ --bandwidth_bits $(BANDWIDTH) \ --num_elem_word $(NUM_ELEM_WORD) \ --elem_width $(ELEM_WIDTH) \ diff --git a/config.mk b/config.mk index acd48fc..f6c04bf 100644 --- a/config.mk +++ b/config.mk @@ -9,10 +9,10 @@ # Datamover hw config # ####################### -BANDWIDTH = 32 # in bits +BANDWIDTH = 128 # in bits WORD_WIDTH = 32 # in bits ELEM_WIDTH = 8 # in bits -MEMORY_SIZE = 1024 # in words +MEMORY_SIZE = 8192 # in words TRANSP_MODE = 1 # 0 = none, 1 = 1 elem, 2 = 2 elem, 4 = 4 elem @@ -26,8 +26,8 @@ NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of ######################### # Base matrix dimensions (in elements) -MATRIX_SIZE_N ?= 8 # Matrix width in elements -MATRIX_SIZE_M ?= 8 # Matrix height in elements +MATRIX_SIZE_N ?= 128 # Matrix width in elements +MATRIX_SIZE_M ?= 64 # Matrix height in elements # Derived stride calculations # ELEM_STRIDE_D0 := $(ELEM_WIDTH) # Element-to-element stride in bits @@ -42,16 +42,15 @@ STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # $(MATRIX_SIZE_N) # Elements -> manually compute "next row" stride STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # Total memory accesses -STIM_WRITE_BASE_ADDR ?= 1024 # Element-addressed -TEMP_MULT1 := $(shell echo $$(($(MATRIX_SIZE_M) * $(TRANSP_MODE)))) # $(shell echo $$(($(STIM_READ_D1_LENGTH) * $(TRANSP_MODE)))) -STIM_WRITE_D0_LENGTH ?= $(MATRIX_SIZE_N) # 8 # $(shell echo $$(($(TEMP_MULT1) / $(BANDWIDTH_ELEMS)))) # Transpose: read height becomes write width -STIM_WRITE_D0_STRIDE ?= $(MATRIX_SIZE_M) # 16 #$(BANDWIDTH_ELEMS) # Bytes per transposed row ToDo(cdurrer): elements or bytes? -# TEMP_MULT2 := # $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(BANDWIDTH_ELEMS)))) -STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_M) / $(BANDWIDTH_ELEMS)))) # 4 # $(shell echo $$(($(MATRIX_SIZE_N) / $(TRANSP_MODE)))) # $(shell echo $$(($(TEMP_MULT2) / $(TRANSP_MODE)))) # Transpose: read width becomes write height -STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # $(shell echo $$(($(STIM_WRITE_D0_LENGTH) * $(BANDWIDTH_ELEMS)))) # Elements -STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read +STIM_WRITE_BASE_ADDR ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Element-addressed +STIM_WRITE_D0_LENGTH ?= $(BANDWIDTH_ELEMS) # Transpose tile width corresponds to bandwidth +STIM_WRITE_D0_STRIDE ?= $(MATRIX_SIZE_M) # Transpose: Input matrix height corresponds to output matrix width +STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_M) / $(BANDWIDTH_ELEMS)))) # Transpose: Input matrix height corresponds to output matrix width +STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # Transpose tile height corresponds to bandwidth +STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(STIM_WRITE_D0_LENGTH)))) # D2 length is controlled by total length +STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read -STIM_MEM_SIZE ?= $(MEMORY_SIZE) #65536 +STIM_MEM_SIZE ?= $(MEMORY_SIZE) # in words STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH @@ -79,6 +78,7 @@ $(info STIM_WRITE_D0_LENGTH: $(STIM_WRITE_D0_LENGTH)) $(info STIM_WRITE_D0_STRIDE: $(STIM_WRITE_D0_STRIDE)) $(info STIM_WRITE_D1_LENGTH: $(STIM_WRITE_D1_LENGTH)) $(info STIM_WRITE_D1_STRIDE: $(STIM_WRITE_D1_STRIDE)) +$(info STIM_WRITE_D2_STRIDE: $(STIM_WRITE_D2_STRIDE)) $(info STIM_WRITE_TOT_LENGTH: $(STIM_WRITE_TOT_LENGTH)) $(info STIM_TRANSP_MODE: $(STIM_TRANSP_MODE)) diff --git a/rtl/datamover_top.sv b/rtl/datamover_top.sv index 55dfea2..4540b75 100644 --- a/rtl/datamover_top.sv +++ b/rtl/datamover_top.sv @@ -49,7 +49,7 @@ module datamover_top // We "sacrifice" 1 word of memory interface bandwidth in order to support // realignment at a word boundary if the access are misaligned. - localparam BANDWIDTH_ALIGNED = MISALIGNED_ACCESSES === 0 ? BANDWIDTH : BANDWIDTH-WORD_WIDTH; + localparam BANDWIDTH_ALIGNED = MISALIGNED_ACCESSES == 0 ? BANDWIDTH : BANDWIDTH-WORD_WIDTH; // State for the FSM declared directly in datamover_top. typedef enum { DM_IDLE, DM_STARTING, DM_WORKING, DM_FINISHED } dm_state; @@ -73,7 +73,7 @@ module datamover_top // Data in and data out internal HWPE-Streams. Notice that the data width // is set to 256 bits by default, 32 bits less than the default external - // bandwidth. The additional 32 bits of memory bandwidth are used to + // bandwidth. The additional 32 bits of memory bandwidth are used to // support access to non-word-aligned data packets. // number of elements (in the full bandwidth, not a single bank word) @@ -136,7 +136,7 @@ module datamover_top .data_in ( data_in ), .data_out ( data_out ) ); - + // The slave module exposes a peripheral interconnect HWPE-Periph plug; // in the default configuration, it provides 4 contexts with 11 registers // each, which are exposed into `reg_file.hwpe_params` @@ -213,8 +213,8 @@ module datamover_top always_comb begin streamer_ctrl_cfg = '0; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.dim_enable_1h = '1; - streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.dim_enable_1h = '1; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.dim_enable_1h = 4'b0001; // Reading operation needs 2 dimensions (activating only d0, and d1 is controlled by tot_len) + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.dim_enable_1h = 4'b0011; // Writing operation needs 3 dimensions (activating d0 and d1, d2 is controlled by tot_len) streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.base_addr = reg_file.hwpe_params[DATAMOVER_REG_IN_PTR >> 2]; streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.base_addr = reg_file.hwpe_params[DATAMOVER_REG_OUT_PTR >> 2]; streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.tot_len = reg_file.hwpe_params[DATAMOVER_REG_LEN0 >> 2][11:0]; @@ -242,10 +242,10 @@ module datamover_top reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b001 ? 1 : reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b010 ? 2 : 4; if(reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16] == '0) begin // no leftover - engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; + engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; // ToDo(cdurrer): why? end else begin // in case of leftover, use the reg content as length - engine_ctrl.transp_len = reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16]; + engine_ctrl.transp_len = reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16]; // ToDo(cdurrer): mention this option in config/pkg/TB end end diff --git a/test/hal_datamover.h b/test/hal_datamover.h index 40cfb05..c1cc53e 100644 --- a/test/hal_datamover.h +++ b/test/hal_datamover.h @@ -41,7 +41,7 @@ #define DATAMOVER_SWSYNC 0x18 #define DATAMOVER_URISCY_IMEM 0x1c -// job configuration +// job configuration ToDo(cdurrer): DIFFERENT TO datamover_package.sv! Why? #define DATAMOVER_REGISTER_OFFS 0x40 #define DATAMOVER_REGISTER_CXT0_OFFS 0x80 #define DATAMOVER_REGISTER_CXT1_OFFS 0x120 diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py index 72d806d..b16e4a1 100644 --- a/verif/python/generate_stimuli_test.py +++ b/verif/python/generate_stimuli_test.py @@ -110,6 +110,7 @@ def main(): parser.add_argument("--read_d1_length", type=int, default=4, help="Length for d1 read") parser.add_argument("--write_d0_stride", type=int, default=4, help="Stride for d0 write (in bytes)") parser.add_argument("--write_d1_stride", type=int, default=16, help="Stride for d1 write (in bytes)") + parser.add_argument("--write_d2_stride", type=int, default=64, help="Stride for d2 write (in bytes)") parser.add_argument("--write_d0_length", type=int, default=4, help="Length for d0 write") parser.add_argument("--write_d1_length", type=int, default=4, help="Length for d1 write") parser.add_argument("--bandwidth_bits", type=int, default=4, help="Number of bits per transaction") diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index fc8bb45..6114c91 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -107,13 +107,14 @@ import tb_package::*; logic [31:0] d1_stride; logic [11:0] d0_length; logic [11:0] d1_length; + logic [11:0] d2_stride; logic [11:0] tot_length; } addressgen_t; addressgen_t read_addr, write_addr; - assign read_addr = '{`STIM_READ_BASE_ADDR, `STIM_READ_D0_STRIDE, `STIM_READ_D1_STRIDE, `STIM_READ_D0_LENGTH, `STIM_READ_D1_LENGTH, `STIM_READ_TOT_LENGTH}; - assign write_addr = '{`STIM_WRITE_BASE_ADDR, `STIM_WRITE_D0_STRIDE, `STIM_WRITE_D1_STRIDE, `STIM_WRITE_D0_LENGTH, `STIM_WRITE_D1_LENGTH, `STIM_WRITE_TOT_LENGTH}; + assign read_addr = '{`STIM_READ_BASE_ADDR, `STIM_READ_D0_STRIDE, `STIM_READ_D1_STRIDE, `STIM_READ_D0_LENGTH, `STIM_READ_D1_LENGTH, '0, `STIM_READ_TOT_LENGTH}; + assign write_addr = '{`STIM_WRITE_BASE_ADDR, `STIM_WRITE_D0_STRIDE, `STIM_WRITE_D1_STRIDE, `STIM_WRITE_D0_LENGTH, `STIM_WRITE_D1_LENGTH, `STIM_WRITE_D2_STRIDE, `STIM_WRITE_TOT_LENGTH}; // assign read_addr = '{`STIM_READ_BASE_ADDR, 32'h4, 32'h10, 32'h4, 32'h4, 32'h10}; // assign write_addr = '{32'h40, 32'h4, 32'h10, 32'h4, 32'h4, 32'h10}; @@ -269,7 +270,7 @@ import tb_package::*; periph_write(datamover_package::DATAMOVER_REG_OUT_D0_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.d0_stride, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_OUT_D1_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.d1_stride, clk_i, periph_bus); - periph_write(datamover_package::DATAMOVER_REG_OUT_D2_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, 32'h4, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_OUT_D2_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.d2_stride, clk_i, periph_bus); // Transposition mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) periph_write(datamover_package::DATAMOVER_REG_TRANSP_MODE, datamover_package::DATAMOVER_REGISTER_OFFS, transp_mode_reg, clk_i, periph_bus); From e753fa1647e01bc56842c519d12ac633d1eb5c3c Mon Sep 17 00:00:00 2001 From: cdurrer Date: Wed, 5 Nov 2025 15:21:45 +0100 Subject: [PATCH 13/29] [tb] Added support for TRANSP_MODE configurations 2 and 4 --- config.mk | 30 +++++++++++++-------------- verif/python/generate_stimuli_test.py | 22 +++++++++++++++++++- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/config.mk b/config.mk index f6c04bf..837a114 100644 --- a/config.mk +++ b/config.mk @@ -14,7 +14,7 @@ WORD_WIDTH = 32 # in bits ELEM_WIDTH = 8 # in bits MEMORY_SIZE = 8192 # in words -TRANSP_MODE = 1 # 0 = none, 1 = 1 elem, 2 = 2 elem, 4 = 4 elem +TRANSP_MODE = 4 # 0 = none, 1 = 1 elem, 2 = 2 elem, 4 = 4 elem # Derived constants from basic parameters # BANDWIDTH_WORDS := $(shell echo $$(($(BANDWIDTH) / $(WORD_WIDTH)))) # Number of words per bandwidth @@ -26,8 +26,8 @@ NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of ######################### # Base matrix dimensions (in elements) -MATRIX_SIZE_N ?= 128 # Matrix width in elements -MATRIX_SIZE_M ?= 64 # Matrix height in elements +MATRIX_SIZE_M ?= 128 # Matrix height in elements +MATRIX_SIZE_N ?= 64 # Matrix width in elements # Derived stride calculations # ELEM_STRIDE_D0 := $(ELEM_WIDTH) # Element-to-element stride in bits @@ -35,22 +35,22 @@ MATRIX_SIZE_M ?= 64 # Matrix height in elements # ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (4*32b) # N (bandwidth) consecutive words are read/written in one transaction -STIM_READ_BASE_ADDR ?= 0 # Element-addressed -STIM_READ_D0_LENGTH ?= $(MATRIX_SIZE_M) # $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # Nof accesses with bandwidth BW per D0-transfer ("row") -STIM_READ_D0_STRIDE ?= $(MATRIX_SIZE_N) # $(BANDWIDTH_ELEMS) # Elements -STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # $(MATRIX_SIZE_M) # Number of full D0-transfers ("rows") -STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # $(MATRIX_SIZE_N) # Elements -> manually compute "next row" stride -STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # Total memory accesses +STIM_READ_BASE_ADDR ?= 0 # Element-addressed +STIM_READ_D0_LENGTH ?= $(MATRIX_SIZE_M) # [Nof accesses with bandwidth BW per D0-transfer ("row")] +STIM_READ_D0_STRIDE ?= $(MATRIX_SIZE_N) # [Elements] +STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # [Number of full D0-transfers ("rows")] +STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -> manually compute "next row" stride +STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] STIM_WRITE_BASE_ADDR ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Element-addressed -STIM_WRITE_D0_LENGTH ?= $(BANDWIDTH_ELEMS) # Transpose tile width corresponds to bandwidth -STIM_WRITE_D0_STRIDE ?= $(MATRIX_SIZE_M) # Transpose: Input matrix height corresponds to output matrix width -STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_M) / $(BANDWIDTH_ELEMS)))) # Transpose: Input matrix height corresponds to output matrix width -STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # Transpose tile height corresponds to bandwidth -STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(STIM_WRITE_D0_LENGTH)))) # D2 length is controlled by total length +STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(BANDWIDTH_ELEMS) / $(TRANSP_MODE)))) # Transpose tile width corresponds to bandwidth +STIM_WRITE_D0_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(TRANSP_MODE)))) # Transpose: Input matrix height corresponds to output matrix width +STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(STIM_WRITE_D0_STRIDE) / $(BANDWIDTH_ELEMS))))# Transpose: Input matrix height corresponds to output matrix width +STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # Transpose tile height corresponds to bandwidth +STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(BANDWIDTH_ELEMS)))) # D2 length is controlled by total length STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read -STIM_MEM_SIZE ?= $(MEMORY_SIZE) # in words +STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py index b16e4a1..10b66f6 100644 --- a/verif/python/generate_stimuli_test.py +++ b/verif/python/generate_stimuli_test.py @@ -2,6 +2,8 @@ import argparse import os +RANDOM_STIMULI = False # If False, counting stimuli are generated in a counting fashion + # OUTPUT_DIR = "generated" # BANDWIDTH = 128 # in bits @@ -26,6 +28,8 @@ # WRITE_D0_LENGTH = READ_D1_LENGTH # WRITE_D1_LENGTH = READ_D0_LENGTH +# ToDo(cdurrer): small matrices (N < BW) not working + def extract_elements_from_word(word, word_width, elem_width): """Extract elements from a word based on the specified widths.""" word_int = int(word, 16) # Convert hex string to integer @@ -37,6 +41,13 @@ def extract_elements_from_word(word, word_width, elem_width): elements.append(elem_val) return elements +def generate_random_hex(size, word_width): + """Generate random word_width hex values.""" + def ceildiv(a, b): + return -(a // -b) + hex_length = ceildiv(word_width, 4) # Each hex digit represents 4 bits + return [f"{random.randint(0, 2**word_width - 1):0{hex_length}X}" for _ in range(size)] + def generate_counting_hex(size, elem_width, word_width): """ Generate counting series of hex values with specified element and word widths. @@ -148,13 +159,22 @@ def main(): # bandwidth width must be a multiple of word size if args.bandwidth_bits % WORD_SIZE_BITS != 0: raise ValueError("bandwidth_bits must be a multiple of the word size (num_elem_word * elem_width).") + # bandwidth width must be a multiple of word size + if ((MATRIX_SIZE_N * ELEM_WIDTH) < args.bandwidth_bits): + raise ValueError("Matrix width (N) in bits must be at least as large as bandwidth_bits.") + # transp_mode must be valid (0=none, 1=1elem, 2=2elem, 4=4elem) + if args.transp_mode not in [0, 1, 2, 4]: + raise ValueError("transp_mode must be 0 (none), 1 (1 elem), 2 (2 elem), or 4 (4 elem).") print(f"Memory Size: {MEMORY_SIZE} entries") print(f"Word Size: {WORD_SIZE_BITS} bits") # memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) - memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH*TRANSP_MODE, WORD_WIDTH) + if RANDOM_STIMULI: + memory = generate_random_hex(MEMORY_SIZE, WORD_SIZE_BITS) # for testing + else: + memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) # for debugging write_file(OUTPUT_DIR, "initial_memory.txt", memory) From 03caf11cabcb0142a1e066f432f335a18ece3195 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Thu, 6 Nov 2025 09:00:49 +0100 Subject: [PATCH 14/29] [tb] Fixed bug with d2_stride parameter --- verif/tb/tb_datamover_top_wrap.sv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index 6114c91..509cb02 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -105,16 +105,16 @@ import tb_package::*; logic [31:0] base_addr; logic [31:0] d0_stride; logic [31:0] d1_stride; + logic [31:0] d2_stride; logic [11:0] d0_length; logic [11:0] d1_length; - logic [11:0] d2_stride; logic [11:0] tot_length; } addressgen_t; addressgen_t read_addr, write_addr; - assign read_addr = '{`STIM_READ_BASE_ADDR, `STIM_READ_D0_STRIDE, `STIM_READ_D1_STRIDE, `STIM_READ_D0_LENGTH, `STIM_READ_D1_LENGTH, '0, `STIM_READ_TOT_LENGTH}; - assign write_addr = '{`STIM_WRITE_BASE_ADDR, `STIM_WRITE_D0_STRIDE, `STIM_WRITE_D1_STRIDE, `STIM_WRITE_D0_LENGTH, `STIM_WRITE_D1_LENGTH, `STIM_WRITE_D2_STRIDE, `STIM_WRITE_TOT_LENGTH}; + assign read_addr = '{`STIM_READ_BASE_ADDR, `STIM_READ_D0_STRIDE, `STIM_READ_D1_STRIDE, '0, `STIM_READ_D0_LENGTH, `STIM_READ_D1_LENGTH, `STIM_READ_TOT_LENGTH}; + assign write_addr = '{`STIM_WRITE_BASE_ADDR, `STIM_WRITE_D0_STRIDE, `STIM_WRITE_D1_STRIDE, `STIM_WRITE_D2_STRIDE, `STIM_WRITE_D0_LENGTH, `STIM_WRITE_D1_LENGTH, `STIM_WRITE_TOT_LENGTH}; // assign read_addr = '{`STIM_READ_BASE_ADDR, 32'h4, 32'h10, 32'h4, 32'h4, 32'h10}; // assign write_addr = '{32'h40, 32'h4, 32'h10, 32'h4, 32'h4, 32'h10}; From e1f3928abb68ef14e106707c8eee92b05ef92f00 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Fri, 7 Nov 2025 15:40:09 +0100 Subject: [PATCH 15/29] [tb] Created a testing setup with presets, configuration validation, running multiple tests consecutively etc. --- CONFIG_USAGE.md | 322 ++++++++++++++++++++++++++ Makefile | 91 +++++++- config.mk | 116 +++++----- config_presets.mk | 124 ++++++++++ rtl/datamover_top.sv | 2 +- verif/python/generate_stimuli_test.py | 69 +++--- verif/python/validate_config.py | 104 +++++++++ verif/tb/tb_datamover_top_wrap.sv | 8 + 8 files changed, 747 insertions(+), 89 deletions(-) create mode 100644 CONFIG_USAGE.md create mode 100644 config_presets.mk create mode 100755 verif/python/validate_config.py diff --git a/CONFIG_USAGE.md b/CONFIG_USAGE.md new file mode 100644 index 0000000..dc23281 --- /dev/null +++ b/CONFIG_USAGE.md @@ -0,0 +1,322 @@ +# Configuration Examples and Usage Guide + +This document describes how to use the flexible configuration system for the datamover HWPE project. + +## 🚀 Quick Commands + +```bash +# Show configuration help +make help-config + +# Validate current config +make validate-config + +# Run with preset +make sim CONFIG_PRESET=small-matrix + +# Override specific parameter +make sim CONFIG_PRESET=transpose-test TRANSP_MODE=4 + +# Test all presets +make test-all-presets + +# Test transpose modes +make test-transpose-modes +``` + +## Quick Start + +### Using Configuration Presets + +Run simulations with predefined configurations: + +```bash +# Small matrix for quick testing +make sim CONFIG_PRESET=small-matrix + +# Medium matrix for moderate testing +make sim CONFIG_PRESET=medium-matrix + +# Large matrix for stress testing +make sim CONFIG_PRESET=large-matrix + +# Transpose-focused testing +make sim CONFIG_PRESET=transpose-test + +# Rectangular matrix testing +make sim CONFIG_PRESET=rect-wide +make sim CONFIG_PRESET=rect-tall +make sim CONFIG_PRESET=rect-narrow +make sim CONFIG_PRESET=rect-elongated +``` + +### Command Line Overrides + +Override specific parameters while keeping preset base: + +```bash +# Use small-matrix preset but change transpose mode +make sim CONFIG_PRESET=small-matrix TRANSP_MODE=2 + +# Use medium-matrix preset but different element width +make sim CONFIG_PRESET=medium-matrix ELEM_WIDTH=16 + +# Override matrix dimensions +make sim CONFIG_PRESET=transpose-test MATRIX_SIZE_M=64 MATRIX_SIZE_N=64 +``` + +### Custom Configuration + +Use completely custom parameters: + +```bash +# Custom configuration via command line +make sim CONFIG_PRESET=custom BANDWIDTH=512 ELEM_WIDTH=16 MATRIX_SIZE_M=128 MATRIX_SIZE_N=128 + +# Or edit config.mk for persistent custom settings +make sim CONFIG_PRESET=custom +``` + +## Configuration Hierarchy + +Parameters are resolved in this order (highest priority first): + +1. **Command line arguments**: `make sim TRANSP_MODE=2` +2. **Preset definitions**: Values from config_presets.mk +3. **Default values**: Fallback values in config.mk + +## Available Presets + +| Preset | Description | Matrix Size | Memory | Transpose | +|--------|-------------|-------------|---------|-----------| +| `small-matrix` | Quick testing | 4x4 | 512w | Mode 1 | +| `medium-matrix` | Moderate testing | 32x32 | 1024w | Mode 1 | +| `large-matrix` | Stress testing | 448x448 | 128KB | Mode 1 | +| `transpose-test` | Transpose focus | 32x32 | 16KB | Mode 2 | +| `rect-wide` | Wide rectangle | 64x256 | 32KB | Mode 1 | +| `rect-tall` | Tall rectangle | 256x64 | 32KB | Mode 2 | +| `rect-narrow` | Narrow rectangle | 16x128 | 8KB | Mode 1 | +| `rect-elongated` | Elongated rectangle | 128x32 | 16KB | Mode 2 | +| `custom` | User-defined | Variable | Variable | Variable | + +## 🔧 Key Parameters + +| Parameter | Values | Description | +|-----------|---------|-------------| +| `TRANSP_MODE` | 0,1,2,4 | Transpose elements per cycle | +| `ELEM_WIDTH` | 8,16,32 | Element width in bits | +| `BANDWIDTH` | 256,512,1024 | Memory bandwidth in bits | +| `MATRIX_SIZE_M` | Any | Matrix height in elements | +| `MATRIX_SIZE_N` | Any | Matrix width in elements | +| `MEMORY_SIZE` | Any | Available memory in words | +| `WORD_WIDTH` | 32 | Word width in bits (typically 32) | + +## Built-in Test Targets + +The system provides several built-in test targets for comprehensive testing: + +```bash +# Test all available presets with detailed reporting (continues through failures) +make test-all-presets + +# Test all transpose modes with transpose-test preset +make test-transpose-modes + +# Validate configuration without running simulation +make validate-config CONFIG_PRESET= +``` + +## Creating New Presets + +Add new presets to `config_presets.mk`: + +```makefile +ifeq ($(CONFIG_PRESET),my-test) + BANDWIDTH = 1024 + WORD_WIDTH = 32 + ELEM_WIDTH = 16 + MEMORY_SIZE = 32768 + TRANSP_MODE = 4 + MATRIX_SIZE_M = 64 + MATRIX_SIZE_N = 32 + CONFIG_DESC = "Custom test for specific use case" +endif +``` + +## Advanced Usage + +### Environment-based Configuration + +Set up your shell environment: + +```bash +# Set default preset for your session +export CONFIG_PRESET=transpose-test + +# Override specific parameters +export TRANSP_MODE=4 +export ELEM_WIDTH=16 + +# Run simulation with environment settings +make sim +``` + +### Makefile Integration + +The system includes the following built-in test targets: + +```makefile +# Built-in targets (already available) +test-all-presets: + # Tests all 8 configuration presets (stops on first failure) + $(MAKE) sim CONFIG_PRESET=small-matrix + $(MAKE) sim CONFIG_PRESET=medium-matrix + $(MAKE) sim CONFIG_PRESET=large-matrix + $(MAKE) sim CONFIG_PRESET=transpose-test + $(MAKE) sim CONFIG_PRESET=rect-wide + $(MAKE) sim CONFIG_PRESET=rect-tall + $(MAKE) sim CONFIG_PRESET=rect-narrow + $(MAKE) sim CONFIG_PRESET=rect-elongated + +test-all-presets: + # Tests all presets with detailed reporting (continues through failures) + # Provides summary of which tests passed/failed + +test-transpose-modes: + # Tests all transpose modes (0,1,2,4) + $(MAKE) sim CONFIG_PRESET=transpose-test TRANSP_MODE=0 + $(MAKE) sim CONFIG_PRESET=transpose-test TRANSP_MODE=1 + $(MAKE) sim CONFIG_PRESET=transpose-test TRANSP_MODE=2 + $(MAKE) sim CONFIG_PRESET=transpose-test TRANSP_MODE=4 +``` + +For additional custom test suites, you can create your own targets: + +```makefile +# Add to your Makefile for custom test suites +test-custom-suite: + @echo "Testing custom configuration suite..." + $(MAKE) sim CONFIG_PRESET=small-matrix TRANSP_MODE=2 + $(MAKE) sim CONFIG_PRESET=medium-matrix ELEM_WIDTH=16 + $(MAKE) sim CONFIG_PRESET=custom BANDWIDTH=1024 MATRIX_SIZE_M=64 MATRIX_SIZE_N=64 +``` + +### Configuration Validation + +The system includes automatic validation: + +- TRANSP_MODE must be 0, 1, 2, or 4 +- Matrix dimensions must fit in available memory +- Bandwidth and element width must be compatible + +### Debug Configuration + +To see all computed values and configuration info: + +```bash +# Show configuration details +make help-config + +# Validate configuration with detailed output +make validate-config CONFIG_PRESET=small-matrix + +# Enable debug output in config.mk (uncomment $(info) lines) +make sim CONFIG_PRESET=small-matrix | grep -E "(BANDWIDTH|MATRIX|STIM)" +``` + +## 💡 Usage Patterns + +```bash +# Development cycle +make sim CONFIG_PRESET=small-matrix # Quick check +make sim CONFIG_PRESET=transpose-test # Feature test +make sim CONFIG_PRESET=large-matrix # Stress test + +# Research/tuning +make sim CONFIG_PRESET=custom BANDWIDTH=1024 ELEM_WIDTH=32 + +# Validation and help +make validate-config CONFIG_PRESET=transpose-test +make help-config +``` + +## Tips and Best Practices + +1. **Start Small**: Use `small-matrix` for initial testing, then scale up +2. **Test Transpose**: Use `transpose-test` preset for transpose functionality +3. **Rectangular Testing**: Use rectangular presets (`rect-wide`, `rect-tall`, etc.) for non-square matrices +4. **Parameter Validation**: Always run `make validate-config` to check computed values +5. **Documentation**: Document custom presets with clear descriptions +6. **Memory Requirements**: Ensure matrix dimensions fit within available memory +7. **Bandwidth Alignment**: Both matrix dimensions should be ≥ BANDWIDTH/ELEM_WIDTH + +## Example Workflows + +### Development Workflow +```bash +# Quick functionality check +make sim CONFIG_PRESET=small-matrix + +# Detailed transpose testing +make sim CONFIG_PRESET=transpose-test + +# Stress testing with large matrices +make sim CONFIG_PRESET=large-matrix +``` + +### CI/Testing Workflow +```bash +# Comprehensive test suite with detailed reporting +make test-all-presets + +# Test specific functionality +make test-transpose-modes +``` + +### Custom Research Configuration +```bash +# Specific research parameters +make sim CONFIG_PRESET=custom \ + BANDWIDTH=1024 \ + ELEM_WIDTH=32 \ + MATRIX_SIZE_M=256 \ + MATRIX_SIZE_N=128 \ + TRANSP_MODE=4 +``` + +## 🔍 System Files + +The configuration system consists of these key files: + +- **`config.mk`** - Main configuration with default values and computed parameters +- **`config_presets.mk`** - Preset definitions for common test scenarios +- **`Makefile`** - Enhanced targets including test-all-presets and validation +- **`verif/python/validate_config.py`** - Python validation script for parameter checking +- **`CONFIG_USAGE.md`** - This comprehensive documentation file + +## Troubleshooting + +### Common Issues + +1. **Invalid TRANSP_MODE**: Must be 0, 1, 2, or 4 + ```bash + make validate-config CONFIG_PRESET=my-preset # Check for errors + ``` + +2. **Memory insufficient**: Matrix too large for available memory + ```bash + # Reduce matrix size or increase MEMORY_SIZE + make sim CONFIG_PRESET=small-matrix # Use smaller preset + ``` + +3. **Bandwidth alignment warnings**: Matrix dimensions not aligned to bandwidth + ```bash + # Adjust matrix dimensions to be multiples of BANDWIDTH/ELEM_WIDTH + make sim MATRIX_SIZE_M=32 MATRIX_SIZE_N=32 # Use aligned dimensions + ``` + +4. **Configuration not taking effect**: Check parameter precedence + ```bash + # Command line overrides presets + make sim CONFIG_PRESET=small-matrix TRANSP_MODE=2 # Override works + ``` diff --git a/Makefile b/Makefile index f77c6ae..e62a0de 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ SYNTH_PATH = synopsys BENDER_TARGETS = -t rtl -t test -t datamover_test -GUI ?= 1 +GUI ?= 0 target ?= sim_tb_datamover_top_wrap VLOG_FLAGS += -svinputport=compat @@ -61,6 +61,90 @@ TESTBENCH_DEFINES += -DELEM_WIDTH=${ELEM_WIDTH} # .PHONY: clean-sim sim-script sim synopsys-script all: testvector sim +# Configuration help target +help-config: + @echo "==========================================" + @echo "Datamover Configuration System" + @echo "==========================================" + @echo "" + @echo "Available presets:" + @echo " small-matrix : 4x4 matrix" + @echo " medium-matrix : 32x32 matrix" + @echo " large-matrix : 448x448 matrix" + @echo " transpose-test : 32x32 matrix" + @echo " rect-wide : 64x256 wide rectangular matrix" + @echo " rect-tall : 256x64 tall rectangular matrix" + @echo " rect-narrow : 16x128 narrow rectangular matrix" + @echo " rect-elongated : 128x32 elongated rectangular matrix" + @echo " custom : User-defined (config.mk default)" + @echo "" + @echo "Usage examples:" + @echo " make sim CONFIG_PRESET=small-matrix" + @echo " make sim CONFIG_PRESET=transpose-test TRANSP_MODE=2" + @echo " make sim MATRIX_SIZE_M=64 MATRIX_SIZE_N=32" + @echo "" + @echo "Test targets:" + @echo " make test-all-presets : Test all presets (detailed reporting)" + @echo " make test-transpose-modes : Test all transpose modes" + @echo "" + @echo "For detailed documentation, see CONFIG_USAGE.md" + @echo "==========================================" + +# Test multiple configurations +test-all-presets: + @echo "Testing all configuration presets..." + @failed_tests=""; \ + for preset in small-matrix medium-matrix large-matrix transpose-test rect-wide rect-tall rect-narrow rect-elongated; do \ + echo "=== Testing CONFIG_PRESET=$$preset ==="; \ + if $(MAKE) sim CONFIG_PRESET=$$preset; then \ + echo "✓ $$preset: PASSED"; \ + else \ + echo "✗ $$preset: FAILED"; \ + failed_tests="$$failed_tests $$preset"; \ + fi; \ + done; \ + if [ -n "$$failed_tests" ]; then \ + echo ""; \ + echo "====== SUMMARY: The following presets FAILED:$$failed_tests ======"; \ + exit 1; \ + else \ + echo ""; \ + echo "====== SUMMARY: All presets PASSED! ======"; \ + fi + +test-transpose-modes: + @echo "Testing all transpose modes..." + @failed_tests=""; \ + for mode in 0 1 2 4; do \ + echo "=== Testing TRANSP_MODE=$$mode ==="; \ + if $(MAKE) sim CONFIG_PRESET=transpose-test TRANSP_MODE=$$mode; then \ + echo "✓ TRANSP_MODE=$$mode: PASSED"; \ + else \ + echo "✗ TRANSP_MODE=$$mode: FAILED"; \ + failed_tests="$$failed_tests $$mode"; \ + fi; \ + done; \ + if [ -n "$$failed_tests" ]; then \ + echo ""; \ + echo "====== SUMMARY: The following transpose modes FAILED:$$failed_tests ======"; \ + exit 1; \ + else \ + echo ""; \ + echo "====== SUMMARY: All transpose modes PASSED! ======"; \ + fi + +# Validate current configuration +validate-config: + @echo "Validating current configuration..." + @python3 verif/python/validate_config.py \ + --bandwidth $(BANDWIDTH) \ + --word_width $(WORD_WIDTH) \ + --elem_width $(ELEM_WIDTH) \ + --memory_size $(MEMORY_SIZE) \ + --transp_mode $(TRANSP_MODE) \ + --matrix_m $(MATRIX_SIZE_M) \ + --matrix_n $(MATRIX_SIZE_N) + clean-sim: rm -rf $(SIM_PATH)/work rm -rf $(SIM_PATH)/compile.tcl @@ -73,7 +157,7 @@ sim-script: clean-sim mkdir -p $(SIM_PATH) $(BENDER_INSTALL_DIR)/bender script vsim $(BENDER_TARGETS) $(TESTBENCH_DEFINES) --vlog-arg="$(VLOG_FLAGS)" >> $(SIM_PATH)/compile.tcl -sim: stimuli sim-script +sim: stimuli sim-script validate-config cd modelsim && \ GUI=$(GUI) $(MAKE) $(target) buildpath=$(ROOT_DIR)/$(SIM_PATH) @@ -88,6 +172,7 @@ stimuli: clean-stimuli --read_d0_length $(STIM_READ_D0_LENGTH) \ --read_d1_stride $(STIM_READ_D1_STRIDE) \ --read_d1_length $(STIM_READ_D1_LENGTH) \ + --read_tot_length $(STIM_READ_TOT_LENGTH) \ --write_base_addr $(STIM_WRITE_BASE_ADDR) \ --write_d0_stride $(STIM_WRITE_D0_STRIDE) \ --write_d0_length $(STIM_WRITE_D0_LENGTH) \ @@ -119,3 +204,5 @@ check-bender: $(BENDER_INSTALL_DIR)/bender: mkdir -p $(BENDER_INSTALL_DIR) && cd $(BENDER_INSTALL_DIR) && \ curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- $(BENDER_VERSION) + +.PHONY: all help-config test-all-presets test-transpose-modes validate-config clean-sim sim-script sim clean-stimuli stimuli bender check-bender diff --git a/config.mk b/config.mk index 837a114..fbfa130 100644 --- a/config.mk +++ b/config.mk @@ -5,33 +5,30 @@ # This file contains the configuration parameter for # the standalone simulation of the datamover HWPE +# Include configuration presets (optional) +-include config_presets.mk + ####################### -# Datamover hw config # +# Datamover HW Config # ####################### -BANDWIDTH = 128 # in bits -WORD_WIDTH = 32 # in bits -ELEM_WIDTH = 8 # in bits -MEMORY_SIZE = 8192 # in words - -TRANSP_MODE = 4 # 0 = none, 1 = 1 elem, 2 = 2 elem, 4 = 4 elem +# Hardware configuration (can be overridden by presets or command line) +BANDWIDTH ?= 512 # in bits, multiple of WORD_WIDTH +WORD_WIDTH ?= 32 # in bits, multiple of ELEM_WIDTH +ELEM_WIDTH ?= 8 # in bits +MEMORY_SIZE ?= 65536 # in words -# Derived constants from basic parameters -# BANDWIDTH_WORDS := $(shell echo $$(($(BANDWIDTH) / $(WORD_WIDTH)))) # Number of words per bandwidth -BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH) / $(ELEM_WIDTH)))) # Number of elements per bandwidth -NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of elements per word +TRANSP_MODE ?= 1 # 0 = none (copy), 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted -######################### -# Stimuli configuration # -######################### +# Input matrix dimensions (in elements) +MATRIX_SIZE_M ?= 256 # Matrix height in elements +MATRIX_SIZE_N ?= 256 # Matrix width in elements -# Base matrix dimensions (in elements) -MATRIX_SIZE_M ?= 128 # Matrix height in elements -MATRIX_SIZE_N ?= 64 # Matrix width in elements +WRITE_BASE_ADDR = $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Element-addressed -# Derived stride calculations -# ELEM_STRIDE_D0 := $(ELEM_WIDTH) # Element-to-element stride in bits -# ROW_STRIDE_BYTES := $(shell echo $$(($(MATRIX_SIZE_N) * $(ELEM_WIDTH) / 8))) # Bytes per row +# Derived constants from basic parameters +BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH) / $(ELEM_WIDTH)))) # Number of elements per bandwidth +NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of elements per word # ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (4*32b) # N (bandwidth) consecutive words are read/written in one transaction @@ -42,43 +39,58 @@ STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -> manually compute "next row" stride STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] -STIM_WRITE_BASE_ADDR ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Element-addressed +STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] +STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 3'b000 = none (copy), 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem, other values: not accepted +STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH + +ifneq "$(strip $(TRANSP_MODE))" "0" +$(info Transpose mode $(TRANSP_MODE) enabled) +STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(BANDWIDTH_ELEMS) / $(TRANSP_MODE)))) # Transpose tile width corresponds to bandwidth STIM_WRITE_D0_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(TRANSP_MODE)))) # Transpose: Input matrix height corresponds to output matrix width STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(STIM_WRITE_D0_STRIDE) / $(BANDWIDTH_ELEMS))))# Transpose: Input matrix height corresponds to output matrix width STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # Transpose tile height corresponds to bandwidth STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(BANDWIDTH_ELEMS)))) # D2 length is controlled by total length STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read - -STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] - -STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem -STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH +else +$(info Transpose mode disabled, using copy mode) +STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) +STIM_WRITE_D0_LENGTH ?= $(STIM_READ_D0_LENGTH) +STIM_WRITE_D0_STRIDE ?= $(STIM_READ_D0_STRIDE) +STIM_WRITE_D1_LENGTH ?= $(STIM_READ_D1_LENGTH) +STIM_WRITE_D1_STRIDE ?= $(STIM_READ_D1_STRIDE) +STIM_WRITE_D2_STRIDE ?= 0 +STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) +endif # Debug: Print computed values (uncomment to see values during make) -$(info BANDWIDTH_ELEMS: $(BANDWIDTH_ELEMS)) -$(info WORD_WIDTH: $(WORD_WIDTH)) -$(info ELEM_WIDTH: $(ELEM_WIDTH)) - -$(info BANDWIDTH_ELEMS: $(BANDWIDTH_ELEMS)) -$(info NUM_ELEM_WORD: $(NUM_ELEM_WORD)) - -$(info MATRIX_SIZE_N: $(MATRIX_SIZE_N)) -$(info MATRIX_SIZE_M: $(MATRIX_SIZE_M)) - -$(info STIM_READ_BASE_ADDR: $(STIM_READ_BASE_ADDR)) -$(info STIM_READ_D0_LENGTH: $(STIM_READ_D0_LENGTH)) -$(info STIM_WRITE_D0_STRIDE: $(STIM_WRITE_D0_STRIDE)) -$(info STIM_READ_D1_LENGTH: $(STIM_READ_D1_LENGTH)) -$(info STIM_READ_D1_STRIDE: $(STIM_READ_D1_STRIDE)) -$(info STIM_READ_TOT_LENGTH: $(STIM_READ_TOT_LENGTH)) - -$(info STIM_WRITE_BASE_ADDR: $(STIM_WRITE_BASE_ADDR)) -$(info STIM_WRITE_D0_LENGTH: $(STIM_WRITE_D0_LENGTH)) -$(info STIM_WRITE_D0_STRIDE: $(STIM_WRITE_D0_STRIDE)) -$(info STIM_WRITE_D1_LENGTH: $(STIM_WRITE_D1_LENGTH)) -$(info STIM_WRITE_D1_STRIDE: $(STIM_WRITE_D1_STRIDE)) -$(info STIM_WRITE_D2_STRIDE: $(STIM_WRITE_D2_STRIDE)) -$(info STIM_WRITE_TOT_LENGTH: $(STIM_WRITE_TOT_LENGTH)) - -$(info STIM_TRANSP_MODE: $(STIM_TRANSP_MODE)) +$(info ========================================) +$(info Hardware Configuration:) +$(info BANDWIDTH: $(BANDWIDTH) bits) +$(info WORD_WIDTH: $(WORD_WIDTH) bits) +$(info ELEM_WIDTH: $(ELEM_WIDTH) bits) +$(info BANDWIDTH_ELEMS: $(BANDWIDTH_ELEMS)) +$(info NUM_ELEM_WORD: $(NUM_ELEM_WORD)) +$(info TRANSP_MODE: $(TRANSP_MODE)) +$(info ) +$(info Matrix Configuration:) +$(info MATRIX_SIZE: $(MATRIX_SIZE_M) x $(MATRIX_SIZE_N)) +$(info MEMORY_SIZE: $(MEMORY_SIZE) words) +$(info ) +$(info Read Configuration:) +$(info BASE_ADDR: $(STIM_READ_BASE_ADDR)) +$(info D0_LENGTH: $(STIM_READ_D0_LENGTH), D0_STRIDE: $(STIM_READ_D0_STRIDE)) +$(info D1_LENGTH: $(STIM_READ_D1_LENGTH), D1_STRIDE: $(STIM_READ_D1_STRIDE)) +$(info TOT_LENGTH: $(STIM_READ_TOT_LENGTH)) +$(info ) +$(info Write Configuration:) +$(info BASE_ADDR: $(STIM_WRITE_BASE_ADDR)) +$(info D0_LENGTH: $(STIM_WRITE_D0_LENGTH), D0_STRIDE: $(STIM_WRITE_D0_STRIDE)) +$(info D1_LENGTH: $(STIM_WRITE_D1_LENGTH), D1_STRIDE: $(STIM_WRITE_D1_STRIDE)) +$(info D2_STRIDE: $(STIM_WRITE_D2_STRIDE)) +$(info TOT_LENGTH: $(STIM_WRITE_TOT_LENGTH)) +$(info ) +$(info Transpose Configuration:) +$(info TRANSP_MODE: $(STIM_TRANSP_MODE)) +$(info TRANSP_LEN: $(STIM_TRANSP_LEN)) +$(info ========================================) diff --git a/config_presets.mk b/config_presets.mk new file mode 100644 index 0000000..9698f57 --- /dev/null +++ b/config_presets.mk @@ -0,0 +1,124 @@ +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +# Configuration Presets for Datamover HWPE +# This file defines named configuration presets for common test scenarios + +######################################### +# Configuration Preset System # +######################################### + +# Available presets: +# - small-matrix : Small 4x4 matrix for quick testing +# - medium-matrix : Medium 32x32 matrix for moderate testing +# - large-matrix : Large 448x448 matrix for stress testing +# - transpose-test : Optimized for transpose functionality verification +# - rect-wide : Wide rectangular matrix (64x256) +# - rect-tall : Tall rectangular matrix (256x64) +# - rect-narrow : Narrow rectangular matrix (16x128) +# - rect-elongated : Elongated rectangular matrix (128x32) +# - custom : User-defined configuration (default) + +# Select configuration preset (can be overridden via command line) +CONFIG_PRESET ?= custom + +# Preset-specific configurations +ifeq ($(CONFIG_PRESET),small-matrix) + BANDWIDTH = 32 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 512 + TRANSP_MODE = 1 + MATRIX_SIZE_M = 4 + MATRIX_SIZE_N = 4 + CONFIG_DESC = "Small 4x4 matrix, 1-element transpose" +endif + +ifeq ($(CONFIG_PRESET),medium-matrix) + BANDWIDTH = 64 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 1024 + TRANSP_MODE = 1 + MATRIX_SIZE_M = 32 + MATRIX_SIZE_N = 32 + CONFIG_DESC = "Medium 32x32 matrix, 1-element transpose" +endif + +ifeq ($(CONFIG_PRESET),large-matrix) + BANDWIDTH = 512 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 131072 + TRANSP_MODE = 1 + MATRIX_SIZE_M = 448 + MATRIX_SIZE_N = 448 + CONFIG_DESC = "Large 448x448 matrix, 1-element transpose" +endif + +ifeq ($(CONFIG_PRESET),transpose-test) + BANDWIDTH = 256 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 16384 + TRANSP_MODE = 2 + MATRIX_SIZE_M = 32 + MATRIX_SIZE_N = 32 + CONFIG_DESC = "32x32 matrix, 2-element transpose" +endif + +ifeq ($(CONFIG_PRESET),rect-wide) + BANDWIDTH = 256 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 32768 + TRANSP_MODE = 1 + MATRIX_SIZE_M = 64 + MATRIX_SIZE_N = 256 + CONFIG_DESC = "Wide rectangular matrix 64x256, 1-element transpose" +endif + +ifeq ($(CONFIG_PRESET),rect-tall) + BANDWIDTH = 256 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 32768 + TRANSP_MODE = 2 + MATRIX_SIZE_M = 256 + MATRIX_SIZE_N = 64 + CONFIG_DESC = "Tall rectangular matrix 256x64, 2-element transpose" +endif + +ifeq ($(CONFIG_PRESET),rect-narrow) + BANDWIDTH = 128 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 8192 + TRANSP_MODE = 1 + MATRIX_SIZE_M = 16 + MATRIX_SIZE_N = 128 + CONFIG_DESC = "Narrow rectangular matrix 16x128, 1-element transpose" +endif + +ifeq ($(CONFIG_PRESET),rect-elongated) + BANDWIDTH = 256 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 16384 + TRANSP_MODE = 2 + MATRIX_SIZE_M = 128 + MATRIX_SIZE_N = 32 + CONFIG_DESC = "Elongated rectangular matrix 128x32, 2-element transpose" +endif + +ifeq ($(CONFIG_PRESET),custom) + # Use values from config.mk or command line overrides + CONFIG_DESC = "Custom user-defined configuration (config.mk)" +endif + +# Print current configuration info +$(info ========================================) +$(info Configuration Preset (CONFIG_PRESET): $(CONFIG_PRESET)) +$(info Description: $(CONFIG_DESC)) +$(info ========================================) diff --git a/rtl/datamover_top.sv b/rtl/datamover_top.sv index 4540b75..9edd810 100644 --- a/rtl/datamover_top.sv +++ b/rtl/datamover_top.sv @@ -213,7 +213,7 @@ module datamover_top always_comb begin streamer_ctrl_cfg = '0; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.dim_enable_1h = 4'b0001; // Reading operation needs 2 dimensions (activating only d0, and d1 is controlled by tot_len) + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.dim_enable_1h = 4'b0011; // Reading operation needs 3 dimensions (activating d0 and d1, d2 is controlled by tot_len) streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.dim_enable_1h = 4'b0011; // Writing operation needs 3 dimensions (activating d0 and d1, d2 is controlled by tot_len) streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.base_addr = reg_file.hwpe_params[DATAMOVER_REG_IN_PTR >> 2]; streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.base_addr = reg_file.hwpe_params[DATAMOVER_REG_OUT_PTR >> 2]; diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py index 10b66f6..bf10969 100644 --- a/verif/python/generate_stimuli_test.py +++ b/verif/python/generate_stimuli_test.py @@ -112,13 +112,14 @@ def transpose(matrix, size_d0, size_d1, transp_mode): def main(): # Parse command-line arguments parser = argparse.ArgumentParser(description="Memory Read/Write Simulation with Word-Aligned Strides") - parser.add_argument("--mem_size", type=int, default=0x30, help="Memory size in entries") + parser.add_argument("--mem_size", type=int, default=0x30, help="Memory size in words") parser.add_argument("--read_base_addr", type=int, default=0x00, help="Base address for read operations") parser.add_argument("--write_base_addr", type=int, default=0x20, help="Base address for write operations") parser.add_argument("--read_d0_stride", type=int, default=4, help="Stride for d0 read (in bytes)") parser.add_argument("--read_d1_stride", type=int, default=16, help="Stride for d1 read (in bytes)") parser.add_argument("--read_d0_length", type=int, default=4, help="Length for d0 read") parser.add_argument("--read_d1_length", type=int, default=4, help="Length for d1 read") + parser.add_argument("--read_tot_length", type=int, default=16, help="Total read length") parser.add_argument("--write_d0_stride", type=int, default=4, help="Stride for d0 write (in bytes)") parser.add_argument("--write_d1_stride", type=int, default=16, help="Stride for d1 write (in bytes)") parser.add_argument("--write_d2_stride", type=int, default=64, help="Stride for d2 write (in bytes)") @@ -155,16 +156,19 @@ def main(): # num_elem_word must be power of two and greater than zero if args.num_elem_word & (args.num_elem_word - 1) != 0 or args.num_elem_word <= 0: - raise ValueError("num_elem_word must be a power of two and greater than zero.") + raise ValueError("[GM] num_elem_word must be a power of two and greater than zero.") # bandwidth width must be a multiple of word size if args.bandwidth_bits % WORD_SIZE_BITS != 0: - raise ValueError("bandwidth_bits must be a multiple of the word size (num_elem_word * elem_width).") + raise ValueError("[GM] bandwidth_bits must be a multiple of the word size (num_elem_word * elem_width).") # bandwidth width must be a multiple of word size if ((MATRIX_SIZE_N * ELEM_WIDTH) < args.bandwidth_bits): - raise ValueError("Matrix width (N) in bits must be at least as large as bandwidth_bits.") + raise ValueError("[GM] Matrix width (N) in bits must be at least as large as bandwidth_bits.") # transp_mode must be valid (0=none, 1=1elem, 2=2elem, 4=4elem) if args.transp_mode not in [0, 1, 2, 4]: - raise ValueError("transp_mode must be 0 (none), 1 (1 elem), 2 (2 elem), or 4 (4 elem).") + raise ValueError("[GM] transp_mode must be 0 (none), 1 (1 elem), 2 (2 elem), or 4 (4 elem).") + # read_tot_length must not exceed 12-bit register capacity (4096) + if ((args.read_tot_length >= 4096) & (TRANSP_MODE != 0)): + raise ValueError("[GM] read_tot_length (MxN / BW_ELEM) must be less than 4096 in transpose mode (12-bit register limit).") print(f"Memory Size: {MEMORY_SIZE} entries") @@ -190,39 +194,36 @@ def main(): input_matrix.append(row) # Print input matrix - print("Input Matrix:") - for i, row in enumerate(input_matrix): - print(f"Row {i}: {row}") - print("\n") - for i, row in enumerate(input_matrix): - print(f"Row {i}: {[format(elem, 'X') for elem in row]}") - - # for d0 in range(d0_length): - # for d1 in range(d1_length): - # input_matrix[d0][d1] = d0*d1_length + d1 # print("Input Matrix:") - # for row in input_matrix: - # print(row) - - transposed_matrix = transpose(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, TRANSP_MODE) - print("\nTransposed Matrix:") - for i, row in enumerate(transposed_matrix): - print(f"Row {i}: {row}") - print("\n") - for i, row in enumerate(transposed_matrix): - print(f"Row {i}: {[format(elem, 'X') for elem in row]}") - - # Convert transposed matrix back to words - transposed_hex_words = matrix_to_hex_words(transposed_matrix, ELEM_WIDTH, WORD_WIDTH) - # print(f"\nTransposed Matrix as Hex Words:") - # for i, word in enumerate(transposed_hex_words): + # # for i, row in enumerate(input_matrix): + # # print(f"Row {i}: {row}") + # # print("\n") + # for i, row in enumerate(input_matrix): + # print(f"Row {i}: {[format(elem, 'X') for elem in row]}") + + if TRANSP_MODE != 0: + transposed_matrix = transpose(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, TRANSP_MODE) + # print("\nTransposed Matrix:") + # # for i, row in enumerate(transposed_matrix): + # # print(f"Row {i}: {row}") + # # print("\n") + # for i, row in enumerate(transposed_matrix): + # print(f"Row {i}: {[format(elem, 'X') for elem in row]}") + output_matrix = transposed_matrix + else: + output_matrix = input_matrix + + # # Convert output matrix back to words + output_hex_words = matrix_to_hex_words(output_matrix, ELEM_WIDTH, WORD_WIDTH) + # print(f"\nOutput Matrix as Hex Words:") + # for i, word in enumerate(output_hex_words): # print(f"Word {i}: {word}") - # Write back transposed matrix to memory at WRITE_BASE_ADDR - for i, word in enumerate(transposed_hex_words): - print(f"Writing word {word} to memory address {WRITE_BASE_ADDR + i * (WORD_WIDTH // 8)}") + # Write back output matrix to memory at WRITE_BASE_ADDR + for i, word in enumerate(output_hex_words): memory[(WRITE_BASE_ADDR // (WORD_WIDTH // 8) + i)] = word - # print(f"Writing word {word} to memory address {WRITE_BASE_ADDR + i}") + # print(f"Writing word {word} to memory address [Byte-address] {WRITE_BASE_ADDR + i * (WORD_WIDTH // 8)}") + # print(f"Writing word {word} to memory address [Word-address] {WRITE_BASE_ADDR + i}") write_file(OUTPUT_DIR, "updated_memory.txt", memory) diff --git a/verif/python/validate_config.py b/verif/python/validate_config.py new file mode 100755 index 0000000..aca2c8e --- /dev/null +++ b/verif/python/validate_config.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +""" +Configuration validation script for datamover HWPE +Validates that configuration parameters are compatible and reasonable +""" + +import sys +import argparse + +def validate_config(bandwidth, word_width, elem_width, memory_size, + transp_mode, matrix_m, matrix_n): + """Validate configuration parameters""" + errors = [] + warnings = [] + + # Basic parameter validation + if bandwidth % word_width != 0: + errors.append(f"BANDWIDTH ({bandwidth}) must be divisible by WORD_WIDTH ({word_width})") + + if word_width % elem_width != 0: + errors.append(f"WORD_WIDTH ({word_width}) must be divisible by ELEM_WIDTH ({elem_width})") + + if transp_mode not in [0, 1, 2, 4]: + errors.append(f"TRANSP_MODE ({transp_mode}) must be 0, 1, 2, or 4") + + # Computed values + bandwidth_elems = bandwidth // elem_width + num_elem_word = word_width // elem_width + + # Memory requirements + matrix_elements = matrix_m * matrix_n + matrix_words = (matrix_elements * elem_width + word_width - 1) // word_width + total_memory_needed = matrix_words * 2 # Input + output matrices + + if total_memory_needed > memory_size: + errors.append(f"Memory size ({memory_size} words) insufficient for matrices " + f"({total_memory_needed} words needed for {matrix_m}x{matrix_n} input+output)") + + # Matrix dimension alignment errors + if matrix_n % bandwidth_elems != 0: + errors.append(f"Matrix width ({matrix_n}) not aligned to bandwidth " + f"({bandwidth_elems} elements)") + + if matrix_m % bandwidth_elems != 0: + errors.append(f"Matrix height ({matrix_m}) not aligned to bandwidth " + f"({bandwidth_elems} elements)") + + # Transpose-specific validation + if transp_mode > 0: + if bandwidth_elems % transp_mode != 0: + errors.append(f"Bandwidth elements ({bandwidth_elems}) must be divisible " + f"by TRANSP_MODE ({transp_mode})") + + return errors, warnings + +def main(): + parser = argparse.ArgumentParser(description="Validate datamover configuration") + parser.add_argument("--bandwidth", type=int, required=True) + parser.add_argument("--word_width", type=int, required=True) + parser.add_argument("--elem_width", type=int, required=True) + parser.add_argument("--memory_size", type=int, required=True) + parser.add_argument("--transp_mode", type=int, required=True) + parser.add_argument("--matrix_m", type=int, required=True) + parser.add_argument("--matrix_n", type=int, required=True) + + args = parser.parse_args() + + errors, warnings = validate_config( + args.bandwidth, args.word_width, args.elem_width, args.memory_size, + args.transp_mode, args.matrix_m, args.matrix_n + ) + + # Print results + if warnings: + print("WARNINGS:") + for warning in warnings: + print(f" - {warning}") + print() + + if errors: + print("ERRORS:") + for error in errors: + print(f" - {error}") + print() + print("Configuration validation FAILED!") + return 1 + else: + print("Configuration validation PASSED!") + + # Print computed values + bandwidth_elems = args.bandwidth // args.elem_width + num_elem_word = args.word_width // args.elem_width + matrix_words = (args.matrix_m * args.matrix_n * args.elem_width + args.word_width - 1) // args.word_width + + print(f"\nComputed values:") + print(f" Elements per bandwidth: {bandwidth_elems}") + print(f" Elements per word: {num_elem_word}") + print(f" Matrix memory usage: {matrix_words} words ({matrix_words * 2} total)") + print(f" Memory utilization: {(matrix_words * 2 * 100) // args.memory_size}%") + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index 509cb02..40cf839 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -295,6 +295,14 @@ import tb_package::*; error_status ); + // Check if there were any errors and fail the simulation if so + if (error_status != 0) begin + $error("Test FAILED: Output mismatch detected (error_status = %0d)", error_status); + $fatal(1, "Simulation terminated due to output verification failure"); + end else begin + $info("Test PASSED: All output verification checks successful"); + end + $finish; end : main_execution From 6b2a5acb570372f76b3746dfa4ca724c6f3a4093 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Tue, 11 Nov 2025 11:01:20 +0100 Subject: [PATCH 16/29] [tb] Improved testing flow; added parameter grid tests, improved error reporting --- CONFIG_USAGE.md | 37 +++++++++++++-------- Makefile | 48 ++++++++++++++++++++++++--- README.md | 32 ++++++++++++++++++ config_presets.mk | 18 +++++----- verif/python/generate_stimuli_test.py | 2 +- verif/tb/tb_datamover_top_wrap.sv | 4 ++- 6 files changed, 111 insertions(+), 30 deletions(-) diff --git a/CONFIG_USAGE.md b/CONFIG_USAGE.md index dc23281..d2c1ec0 100644 --- a/CONFIG_USAGE.md +++ b/CONFIG_USAGE.md @@ -6,7 +6,7 @@ This document describes how to use the flexible configuration system for the dat ```bash # Show configuration help -make help-config +make help # Validate current config make validate-config @@ -22,6 +22,9 @@ make test-all-presets # Test transpose modes make test-transpose-modes + +# Test configuration combinations (grid) +make test-config-grid ``` ## Quick Start @@ -89,14 +92,14 @@ Parameters are resolved in this order (highest priority first): | Preset | Description | Matrix Size | Memory | Transpose | |--------|-------------|-------------|---------|-----------| -| `small-matrix` | Quick testing | 4x4 | 512w | Mode 1 | -| `medium-matrix` | Moderate testing | 32x32 | 1024w | Mode 1 | -| `large-matrix` | Stress testing | 448x448 | 128KB | Mode 1 | -| `transpose-test` | Transpose focus | 32x32 | 16KB | Mode 2 | -| `rect-wide` | Wide rectangle | 64x256 | 32KB | Mode 1 | -| `rect-tall` | Tall rectangle | 256x64 | 32KB | Mode 2 | -| `rect-narrow` | Narrow rectangle | 16x128 | 8KB | Mode 1 | -| `rect-elongated` | Elongated rectangle | 128x32 | 16KB | Mode 2 | +| `small-matrix` | Quick testing | 4x4 | 2KB | Mode 1 | +| `medium-matrix` | Moderate testing | 64x64 | 16KB | Mode 1 | +| `large-matrix` | Stress testing | 448x448 | 512KB | Mode 1 | +| `transpose-test` | Transpose focus | 32x32 | 64KB | Mode 2 | +| `rect-wide` | Wide rectangle | 64x256 | 128KB | Mode 4 | +| `rect-tall` | Tall rectangle | 256x64 | 128KB | Mode 2 | +| `rect-narrow` | Narrow rectangle | 16x128 | 32KB | Mode 1 | +| `rect-elongated` | Elongated rectangle | 128x32 | 64KB | Mode 2 | | `custom` | User-defined | Variable | Variable | Variable | ## 🔧 Key Parameters @@ -104,12 +107,12 @@ Parameters are resolved in this order (highest priority first): | Parameter | Values | Description | |-----------|---------|-------------| | `TRANSP_MODE` | 0,1,2,4 | Transpose elements per cycle | -| `ELEM_WIDTH` | 8,16,32 | Element width in bits | -| `BANDWIDTH` | 256,512,1024 | Memory bandwidth in bits | +| `ELEM_WIDTH` | 8 | Element width in bits | +| `BANDWIDTH` | 64,128,256,512,1024 | Memory bandwidth in bits | | `MATRIX_SIZE_M` | Any | Matrix height in elements | | `MATRIX_SIZE_N` | Any | Matrix width in elements | | `MEMORY_SIZE` | Any | Available memory in words | -| `WORD_WIDTH` | 32 | Word width in bits (typically 32) | +| `WORD_WIDTH` | 16,32,64 | Word width in bits (typically 32) | ## Built-in Test Targets @@ -122,6 +125,9 @@ make test-all-presets # Test all transpose modes with transpose-test preset make test-transpose-modes +# Test configuration parameter combinations (bandwidth/transpose/word width grid) +make test-config-grid + # Validate configuration without running simulation make validate-config CONFIG_PRESET= ``` @@ -215,7 +221,7 @@ To see all computed values and configuration info: ```bash # Show configuration details -make help-config +make help # Validate configuration with detailed output make validate-config CONFIG_PRESET=small-matrix @@ -237,7 +243,7 @@ make sim CONFIG_PRESET=custom BANDWIDTH=1024 ELEM_WIDTH=32 # Validation and help make validate-config CONFIG_PRESET=transpose-test -make help-config +make help ``` ## Tips and Best Practices @@ -271,6 +277,9 @@ make test-all-presets # Test specific functionality make test-transpose-modes + +# Comprehensive parameter grid testing +make test-config-grid ``` ### Custom Research Configuration diff --git a/Makefile b/Makefile index e62a0de..9827c90 100644 --- a/Makefile +++ b/Makefile @@ -62,14 +62,14 @@ TESTBENCH_DEFINES += -DELEM_WIDTH=${ELEM_WIDTH} all: testvector sim # Configuration help target -help-config: +help: @echo "==========================================" @echo "Datamover Configuration System" @echo "==========================================" @echo "" @echo "Available presets:" @echo " small-matrix : 4x4 matrix" - @echo " medium-matrix : 32x32 matrix" + @echo " medium-matrix : 64x64 matrix" @echo " large-matrix : 448x448 matrix" @echo " transpose-test : 32x32 matrix" @echo " rect-wide : 64x256 wide rectangular matrix" @@ -84,8 +84,9 @@ help-config: @echo " make sim MATRIX_SIZE_M=64 MATRIX_SIZE_N=32" @echo "" @echo "Test targets:" - @echo " make test-all-presets : Test all presets (detailed reporting)" - @echo " make test-transpose-modes : Test all transpose modes" + @echo " make test-all-presets : Test all presets (detailed reporting)" + @echo " make test-transpose-modes : Test all transpose modes" + @echo " make test-config-grid : Test all bandwidth/transpose/word width combinations" @echo "" @echo "For detailed documentation, see CONFIG_USAGE.md" @echo "==========================================" @@ -133,6 +134,43 @@ test-transpose-modes: echo "====== SUMMARY: All transpose modes PASSED! ======"; \ fi + +test-config-grid: + @echo "Testing configuration parameter combinations (grid)..." + @failed_tests=""; \ + total_tests=0; \ + passed_tests=0; \ + for bandwidth in 256 512; do \ + for mode in 0 1 2 4; do \ + for word_width in 16 32 64; do \ + total_tests=$$((total_tests + 1)); \ + echo "=== Testing BANDWIDTH=$$bandwidth TRANSP_MODE=$$mode WORD_WIDTH=$$word_width ==="; \ + if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth TRANSP_MODE=$$mode WORD_WIDTH=$$word_width; then \ + echo "✓ BANDWIDTH=$$bandwidth TRANSP_MODE=$$mode WORD_WIDTH=$$word_width: PASSED"; \ + passed_tests=$$((passed_tests + 1)); \ + else \ + echo "✗ BANDWIDTH=$$bandwidth TRANSP_MODE=$$mode WORD_WIDTH=$$word_width: FAILED"; \ + failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/TRANSP_MODE=$$mode/WORD_WIDTH=$$word_width"; \ + fi; \ + done; \ + done; \ + done; \ + echo ""; \ + echo "====== TEST SUMMARY ======"; \ + echo "Total tests: $$total_tests"; \ + echo "Passed: $$passed_tests"; \ + echo "Failed: $$((total_tests - passed_tests))"; \ + if [ -n "$$failed_tests" ]; then \ + echo ""; \ + echo "FAILED combinations:$$failed_tests"; \ + exit 1; \ + else \ + echo ""; \ + echo "====== SUMMARY: All bandwidth/transpose/word width combinations PASSED! ======"; \ + fi + + + # Validate current configuration validate-config: @echo "Validating current configuration..." @@ -205,4 +243,4 @@ $(BENDER_INSTALL_DIR)/bender: mkdir -p $(BENDER_INSTALL_DIR) && cd $(BENDER_INSTALL_DIR) && \ curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- $(BENDER_VERSION) -.PHONY: all help-config test-all-presets test-transpose-modes validate-config clean-sim sim-script sim clean-stimuli stimuli bender check-bender +.PHONY: all help test-all-presets test-transpose-modes test-config-grid validate-config clean-sim sim-script sim clean-stimuli stimuli bender check-bender diff --git a/README.md b/README.md index 0dbad9b..d9880de 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,38 @@ make sim ``` By default QuestaSim GUI is active. You can simulate the RTL in CLI mode with `GUI=0 make sim`. +## Testing and Validation + +The datamover HWPE provides several test targets for comprehensive validation: + +### Configuration Testing +```sh +# Test all configuration presets +make test-all-presets + +# Test all transpose modes +make test-transpose-modes + +# Test configuration parameter combinations (grid testing) +make test-config-grid + +# Show available configurations +make help +``` + +### Configuration Presets +The system includes predefined test configurations: +- `small-matrix`: 4×4 matrix (quick testing) +- `medium-matrix`: 64×64 matrix (moderate testing) +- `large-matrix`: 448×448 matrix (stress testing) +- `transpose-test`: 32×32 matrix (transpose focus) +- `rect-wide`: 64×256 matrix (4-element transpose) +- `rect-tall`: 256×64 matrix (2-element transpose) +- `rect-narrow`: 16×128 matrix (1-element transpose) +- `rect-elongated`: 128×32 matrix (2-element transpose) + +For detailed configuration documentation, see `CONFIG_USAGE.md`. + ## Test Results If the tests pass successfully, you should see the following message displayed at the end: ``` diff --git a/config_presets.mk b/config_presets.mk index 9698f57..312bb4d 100644 --- a/config_presets.mk +++ b/config_presets.mk @@ -11,7 +11,7 @@ # Available presets: # - small-matrix : Small 4x4 matrix for quick testing -# - medium-matrix : Medium 32x32 matrix for moderate testing +# - medium-matrix : Medium 64x64 matrix for moderate testing # - large-matrix : Large 448x448 matrix for stress testing # - transpose-test : Optimized for transpose functionality verification # - rect-wide : Wide rectangular matrix (64x256) @@ -36,14 +36,14 @@ ifeq ($(CONFIG_PRESET),small-matrix) endif ifeq ($(CONFIG_PRESET),medium-matrix) - BANDWIDTH = 64 + BANDWIDTH = 128 WORD_WIDTH = 32 ELEM_WIDTH = 8 - MEMORY_SIZE = 1024 + MEMORY_SIZE = 4096 TRANSP_MODE = 1 - MATRIX_SIZE_M = 32 - MATRIX_SIZE_N = 32 - CONFIG_DESC = "Medium 32x32 matrix, 1-element transpose" + MATRIX_SIZE_M = 64 + MATRIX_SIZE_N = 64 + CONFIG_DESC = "Medium 64x64 matrix, 1-element transpose" endif ifeq ($(CONFIG_PRESET),large-matrix) @@ -70,13 +70,13 @@ endif ifeq ($(CONFIG_PRESET),rect-wide) BANDWIDTH = 256 - WORD_WIDTH = 32 + WORD_WIDTH = 16 ELEM_WIDTH = 8 MEMORY_SIZE = 32768 - TRANSP_MODE = 1 + TRANSP_MODE = 4 MATRIX_SIZE_M = 64 MATRIX_SIZE_N = 256 - CONFIG_DESC = "Wide rectangular matrix 64x256, 1-element transpose" + CONFIG_DESC = "Wide rectangular matrix 64x256, 4-element transpose" endif ifeq ($(CONFIG_PRESET),rect-tall) diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py index bf10969..3de36a1 100644 --- a/verif/python/generate_stimuli_test.py +++ b/verif/python/generate_stimuli_test.py @@ -2,7 +2,7 @@ import argparse import os -RANDOM_STIMULI = False # If False, counting stimuli are generated in a counting fashion +RANDOM_STIMULI = True # If False, counting stimuli are generated in a counting fashion # OUTPUT_DIR = "generated" diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index 40cf839..2208c3e 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -298,9 +298,11 @@ import tb_package::*; // Check if there were any errors and fail the simulation if so if (error_status != 0) begin $error("Test FAILED: Output mismatch detected (error_status = %0d)", error_status); - $fatal(1, "Simulation terminated due to output verification failure"); + $display("DATAMOVER_TEST_FAILED"); + $stop(1); end else begin $info("Test PASSED: All output verification checks successful"); + $display("DATAMOVER_TEST_PASSED"); end $finish; From 63352879c99d9b3564b48996048b556da690082d Mon Sep 17 00:00:00 2001 From: cdurrer Date: Tue, 11 Nov 2025 11:02:20 +0100 Subject: [PATCH 17/29] [rtl] Fixed issue which prevented WORD_WIDTH > 32 from working properly --- rtl/datamover_engine.sv | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rtl/datamover_engine.sv b/rtl/datamover_engine.sv index b13255e..a6cfd78 100644 --- a/rtl/datamover_engine.sv +++ b/rtl/datamover_engine.sv @@ -58,7 +58,7 @@ module datamover_engine logic [NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] data_out_unrolled; logic data_out_valid; logic data_out_ready; - + // FSM: WRITE -> READ on input handshake at end of write, READ -> WRITE on output handshake at end of read always_comb begin @@ -165,11 +165,11 @@ module datamover_engine // the configurations are: 8b transpose, 16b transpose, 32b transpose. We assume // that transposes >= 64b can be done efficiently by Snitch processors through SSRs // and those < 8b are not interesting in our use case. - localparam MAX_SHIFTING = 4; // in "number of elements per word" + localparam MAX_SHIFTING = (NUM_ELEM_WORD > 4) ? NUM_ELEM_WORD : 4; // e.g., in a classical configuration (ELEM_WIDTH = 8), MAX_SHIFTING is // in bytes, i.e., "4" for 32b transpose (includes shifting by 0 bytes) logic [MAX_SHIFTING-1:0][NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] data_in_shifted; - + for(genvar ii=0; ii Date: Wed, 12 Nov 2025 16:40:00 +0100 Subject: [PATCH 18/29] [rtl,tb] Implemented data layout conversion mode for CIM and corresponding tests --- CONFIG_USAGE.md | 76 +++++++++++++--- Makefile | 116 +++++++++++++++++++----- README.md | 8 +- config.mk | 92 +++++++++++++++---- config_presets.mk | 126 ++++++++++++++++++++++++-- verif/python/generate_stimuli_test.py | 65 ++++++------- verif/python/validate_config.py | 77 +++++++++++++--- verif/tb/tb_datamover_top_wrap.sv | 4 +- 8 files changed, 456 insertions(+), 108 deletions(-) diff --git a/CONFIG_USAGE.md b/CONFIG_USAGE.md index d2c1ec0..ce55d4f 100644 --- a/CONFIG_USAGE.md +++ b/CONFIG_USAGE.md @@ -24,7 +24,8 @@ make test-all-presets make test-transpose-modes # Test configuration combinations (grid) -make test-config-grid +make test-transpose-grid +make test-cim-grid ``` ## Quick Start @@ -51,6 +52,15 @@ make sim CONFIG_PRESET=rect-wide make sim CONFIG_PRESET=rect-tall make sim CONFIG_PRESET=rect-narrow make sim CONFIG_PRESET=rect-elongated + +# Copy mode testing +make sim CONFIG_PRESET=copy-small +make sim CONFIG_PRESET=copy-medium + +# CIM mode testing +make sim CONFIG_PRESET=cim-small +make sim CONFIG_PRESET=cim-medium +make sim CONFIG_PRESET=cim-large ``` ### Command Line Overrides @@ -90,23 +100,57 @@ Parameters are resolved in this order (highest priority first): ## Available Presets -| Preset | Description | Matrix Size | Memory | Transpose | -|--------|-------------|-------------|---------|-----------| -| `small-matrix` | Quick testing | 4x4 | 2KB | Mode 1 | -| `medium-matrix` | Moderate testing | 64x64 | 16KB | Mode 1 | -| `large-matrix` | Stress testing | 448x448 | 512KB | Mode 1 | -| `transpose-test` | Transpose focus | 32x32 | 64KB | Mode 2 | -| `rect-wide` | Wide rectangle | 64x256 | 128KB | Mode 4 | -| `rect-tall` | Tall rectangle | 256x64 | 128KB | Mode 2 | -| `rect-narrow` | Narrow rectangle | 16x128 | 32KB | Mode 1 | -| `rect-elongated` | Elongated rectangle | 128x32 | 64KB | Mode 2 | +| Preset | Description | Matrix Size | Memory | Mode | +|--------|-------------|-------------|--------|------| +| `small-matrix` | Quick testing | 4x4 | 2KB | Transpose | +| `medium-matrix` | Moderate testing | 64x64 | 16KB | Transpose | +| `large-matrix` | Stress testing | 448x448 | 512KB | Transpose | +| `transpose-test` | Transpose focus | 32x32 | 64KB | Transpose | +| `rect-wide` | Wide rectangle | 64x256 | 128KB | Transpose | +| `rect-tall` | Tall rectangle | 256x64 | 128KB | Transpose | +| `rect-narrow` | Narrow rectangle | 16x128 | 32KB | Transpose | +| `rect-elongated` | Elongated rectangle | 128x32 | 64KB | Transpose | +| `copy-small` | Copy mode testing | 4x4 | 2KB | Copy | +| `copy-medium` | Copy mode testing | 64x64 | 16KB | Copy | +| `cim-small` | CIM mode testing | 32x128 | 32KB | CIM | +| `cim-medium` | CIM mode testing | 64x256 | 64KB | CIM | +| `cim-large` | CIM mode testing | 128x512 | 256KB | CIM | | `custom` | User-defined | Variable | Variable | Variable | -## 🔧 Key Parameters +## � Datamover Modes + +The datamover supports three main operation modes: + +### Copy Mode (DATAMOVER_MODE=0) +- **Purpose**: Direct memory-to-memory copy operations +- **Use case**: Basic data movement without transformation +- **Presets**: `copy-small`, `copy-medium` +- **Example**: `make sim CONFIG_PRESET=copy-small` + +### Transpose Mode (DATAMOVER_MODE=1) +- **Purpose**: Matrix transposition during data movement +- **Use case**: Data layout transformations for optimized access patterns +- **Transpose elements**: 1, 2, or 4 elements per cycle (`TRANSP_MODE`) +- **Presets**: `small-matrix`, `medium-matrix`, `large-matrix`, `transpose-test`, `rect-*` +- **Example**: `make sim CONFIG_PRESET=transpose-test TRANSP_MODE=2` + +### CIM Mode (DATAMOVER_MODE=2) +- **Purpose**: Compute-In-Memory data layout conversion +- **Use case**: Converting row-major data to CIM accelerator layouts +- **CIM layouts**: A-Layout (`CIM_MODE=0`) or B-Layout (`CIM_MODE=1`) +- **Dimensions**: Configurable `CIM_INNER_DIM` and `CIM_OUTER_DIM` +- **Presets**: `cim-small`, `cim-medium`, `cim-large` +- **Example**: `make sim CONFIG_PRESET=cim-medium` + +## Key Parameters | Parameter | Values | Description | |-----------|---------|-------------| +| `DATAMOVER_MODE` | 0,1,2 | Operation mode (0=Copy, 1=Transpose, 2=CIM) | | `TRANSP_MODE` | 0,1,2,4 | Transpose elements per cycle | +| `CIM_MODE` | 0,1 | CIM layout (0=A-Layout, 1=B-Layout) | +| `CIM_INNER_DIM` | 32,64,... | CIM inner dimension in elements | +| `CIM_OUTER_DIM` | 16,32,64,... | CIM outer dimension in elements | | `ELEM_WIDTH` | 8 | Element width in bits | | `BANDWIDTH` | 64,128,256,512,1024 | Memory bandwidth in bits | | `MATRIX_SIZE_M` | Any | Matrix height in elements | @@ -126,7 +170,10 @@ make test-all-presets make test-transpose-modes # Test configuration parameter combinations (bandwidth/transpose/word width grid) -make test-config-grid +make test-transpose-grid + +# Test CIM configuration parameter combinations (bandwidth/CIM dimensions/word width grid) +make test-cim-grid # Validate configuration without running simulation make validate-config CONFIG_PRESET= @@ -279,7 +326,8 @@ make test-all-presets make test-transpose-modes # Comprehensive parameter grid testing -make test-config-grid +make test-transpose-grid +make test-cim-grid ``` ### Custom Research Configuration diff --git a/Makefile b/Makefile index 9827c90..8382836 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,7 @@ TESTBENCH_DEFINES += -DSTIM_READ_D0_STRIDE=${STIM_READ_D0_STRIDE} TESTBENCH_DEFINES += -DSTIM_READ_D0_LENGTH=${STIM_READ_D0_LENGTH} TESTBENCH_DEFINES += -DSTIM_READ_D1_STRIDE=${STIM_READ_D1_STRIDE} TESTBENCH_DEFINES += -DSTIM_READ_D1_LENGTH=${STIM_READ_D1_LENGTH} +TESTBENCH_DEFINES += -DSTIM_READ_D2_STRIDE=${STIM_READ_D2_STRIDE} TESTBENCH_DEFINES += -DSTIM_READ_TOT_LENGTH=${STIM_READ_TOT_LENGTH} TESTBENCH_DEFINES += -DSTIM_WRITE_BASE_ADDR=${STIM_WRITE_BASE_ADDR} @@ -59,7 +60,7 @@ TESTBENCH_DEFINES += -DELEM_WIDTH=${ELEM_WIDTH} # .PHONY: clean-sim sim-script sim synopsys-script -all: testvector sim +all: sim # Configuration help target help: @@ -68,14 +69,19 @@ help: @echo "==========================================" @echo "" @echo "Available presets:" - @echo " small-matrix : 4x4 matrix" - @echo " medium-matrix : 64x64 matrix" - @echo " large-matrix : 448x448 matrix" - @echo " transpose-test : 32x32 matrix" - @echo " rect-wide : 64x256 wide rectangular matrix" - @echo " rect-tall : 256x64 tall rectangular matrix" - @echo " rect-narrow : 16x128 narrow rectangular matrix" - @echo " rect-elongated : 128x32 elongated rectangular matrix" + @echo " small-matrix : 4x4 matrix (transpose)" + @echo " medium-matrix : 64x64 matrix (transpose)" + @echo " large-matrix : 448x448 matrix (transpose)" + @echo " transpose-test : 32x32 matrix (transpose)" + @echo " rect-wide : 64x256 wide rectangular matrix (transpose)" + @echo " rect-tall : 256x64 tall rectangular matrix (transpose)" + @echo " rect-narrow : 16x128 narrow rectangular matrix (transpose)" + @echo " rect-elongated : 128x32 elongated rectangular matrix (transpose)" + @echo " copy-small : 4x4 matrix (copy mode)" + @echo " copy-medium : 64x64 matrix (copy mode)" + @echo " cim-small : 32x128 matrix (CIM mode)" + @echo " cim-medium : 64x256 matrix (CIM mode)" + @echo " cim-large : 128x512 matrix (CIM mode)" @echo " custom : User-defined (config.mk default)" @echo "" @echo "Usage examples:" @@ -86,7 +92,8 @@ help: @echo "Test targets:" @echo " make test-all-presets : Test all presets (detailed reporting)" @echo " make test-transpose-modes : Test all transpose modes" - @echo " make test-config-grid : Test all bandwidth/transpose/word width combinations" + @echo " make test-transpose-grid : Test all bandwidth/transpose/word width combinations" + @echo " make test-cim-grid : Test all bandwidth/CIM dimension/word width combinations" @echo "" @echo "For detailed documentation, see CONFIG_USAGE.md" @echo "==========================================" @@ -95,7 +102,7 @@ help: test-all-presets: @echo "Testing all configuration presets..." @failed_tests=""; \ - for preset in small-matrix medium-matrix large-matrix transpose-test rect-wide rect-tall rect-narrow rect-elongated; do \ + for preset in small-matrix medium-matrix large-matrix transpose-test rect-wide rect-tall rect-narrow rect-elongated copy-small copy-medium cim-small cim-medium cim-large; do \ echo "=== Testing CONFIG_PRESET=$$preset ==="; \ if $(MAKE) sim CONFIG_PRESET=$$preset; then \ echo "✓ $$preset: PASSED"; \ @@ -116,7 +123,7 @@ test-all-presets: test-transpose-modes: @echo "Testing all transpose modes..." @failed_tests=""; \ - for mode in 0 1 2 4; do \ + for mode in 1 2 4; do \ echo "=== Testing TRANSP_MODE=$$mode ==="; \ if $(MAKE) sim CONFIG_PRESET=transpose-test TRANSP_MODE=$$mode; then \ echo "✓ TRANSP_MODE=$$mode: PASSED"; \ @@ -135,22 +142,22 @@ test-transpose-modes: fi -test-config-grid: +test-transpose-grid: @echo "Testing configuration parameter combinations (grid)..." @failed_tests=""; \ total_tests=0; \ passed_tests=0; \ for bandwidth in 256 512; do \ - for mode in 0 1 2 4; do \ + for transp_mode in 1 2 4; do \ for word_width in 16 32 64; do \ total_tests=$$((total_tests + 1)); \ - echo "=== Testing BANDWIDTH=$$bandwidth TRANSP_MODE=$$mode WORD_WIDTH=$$word_width ==="; \ - if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth TRANSP_MODE=$$mode WORD_WIDTH=$$word_width; then \ - echo "✓ BANDWIDTH=$$bandwidth TRANSP_MODE=$$mode WORD_WIDTH=$$word_width: PASSED"; \ + echo "=== Testing BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width ==="; \ + if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ + echo "✓ BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: PASSED"; \ passed_tests=$$((passed_tests + 1)); \ else \ - echo "✗ BANDWIDTH=$$bandwidth TRANSP_MODE=$$mode WORD_WIDTH=$$word_width: FAILED"; \ - failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/TRANSP_MODE=$$mode/WORD_WIDTH=$$word_width"; \ + echo "✗ BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: FAILED"; \ + failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/TRANSP_MODE=$$transp_mode/WORD_WIDTH=$$word_width"; \ fi; \ done; \ done; \ @@ -169,6 +176,60 @@ test-config-grid: echo "====== SUMMARY: All bandwidth/transpose/word width combinations PASSED! ======"; \ fi +test-cim-grid: + @echo "Testing CIM configuration parameter combinations (grid)..." + @failed_tests=""; \ + total_tests=0; \ + passed_tests=0; \ + for bandwidth in 128 256; do \ + for word_width in 32 64; do \ + for cim_inner_dim in 32 64; do \ + for cim_outer_dim in 32 64; do \ + total_tests=$$((total_tests + 1)); \ + echo "=== Testing BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim ==="; \ + if $(MAKE) sim CONFIG_PRESET=cim-large DATAMOVER_MODE=2 CIM_MODE=0 BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim; then \ + echo "✓ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim: PASSED"; \ + passed_tests=$$((passed_tests + 1)); \ + else \ + echo "✗ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim: FAILED"; \ + failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/WORD_WIDTH=$$word_width/CIM_INNER_DIM=$$cim_inner_dim/CIM_OUTER_DIM=$$cim_outer_dim"; \ + fi; \ + done; \ + done; \ + done; \ + done; \ + for bandwidth in 512; do \ + for word_width in 32 64; do \ + for cim_inner_dim in 64; do \ + for cim_outer_dim in 64; do \ + total_tests=$$((total_tests + 1)); \ + echo "=== Testing BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim ==="; \ + if $(MAKE) sim CONFIG_PRESET=cim-large DATAMOVER_MODE=2 CIM_MODE=0 BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim; then \ + echo "✓ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim: PASSED"; \ + passed_tests=$$((passed_tests + 1)); \ + else \ + echo "✗ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim: FAILED"; \ + failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/WORD_WIDTH=$$word_width/CIM_INNER_DIM=$$cim_inner_dim/CIM_OUTER_DIM=$$cim_outer_dim"; \ + fi; \ + done; \ + done; \ + done; \ + done; \ + echo ""; \ + echo "====== CIM TEST SUMMARY ======"; \ + echo "Total tests: $$total_tests"; \ + echo "Passed: $$passed_tests"; \ + echo "Failed: $$((total_tests - passed_tests))"; \ + if [ -n "$$failed_tests" ]; then \ + echo ""; \ + echo "FAILED CIM combinations:$$failed_tests"; \ + exit 1; \ + else \ + echo ""; \ + echo "====== SUMMARY: All CIM configuration combinations PASSED! ======"; \ + fi + +# ToDo: CIM tests grid # Validate current configuration @@ -179,9 +240,13 @@ validate-config: --word_width $(WORD_WIDTH) \ --elem_width $(ELEM_WIDTH) \ --memory_size $(MEMORY_SIZE) \ + --datamover_mode $(DATAMOVER_MODE) \ --transp_mode $(TRANSP_MODE) \ - --matrix_m $(MATRIX_SIZE_M) \ - --matrix_n $(MATRIX_SIZE_N) + --cim_mode $(CIM_MODE) \ + --cim_inner_dim $(CIM_INNER_DIM) \ + --cim_outer_dim $(CIM_OUTER_DIM) \ + --matrix_size_m $(MATRIX_SIZE_M) \ + --matrix_size_n $(MATRIX_SIZE_N) clean-sim: rm -rf $(SIM_PATH)/work @@ -210,6 +275,7 @@ stimuli: clean-stimuli --read_d0_length $(STIM_READ_D0_LENGTH) \ --read_d1_stride $(STIM_READ_D1_STRIDE) \ --read_d1_length $(STIM_READ_D1_LENGTH) \ + --read_d2_stride $(STIM_READ_D2_STRIDE) \ --read_tot_length $(STIM_READ_TOT_LENGTH) \ --write_base_addr $(STIM_WRITE_BASE_ADDR) \ --write_d0_stride $(STIM_WRITE_D0_STRIDE) \ @@ -220,8 +286,14 @@ stimuli: clean-stimuli --bandwidth_bits $(BANDWIDTH) \ --num_elem_word $(NUM_ELEM_WORD) \ --elem_width $(ELEM_WIDTH) \ + --datamover_mode $(DATAMOVER_MODE) \ --transp_mode $(STIM_TRANSP_MODE) \ --transp_len $(STIM_TRANSP_LEN) \ + --cim_mode $(CIM_MODE) \ + --cim_inner_dim $(CIM_INNER_DIM) \ + --cim_outer_dim $(CIM_OUTER_DIM) \ + --matrix_size_m $(MATRIX_SIZE_M) \ + --matrix_size_n $(MATRIX_SIZE_N) \ --output_dir "verif/python/generated" # Bender @@ -243,4 +315,4 @@ $(BENDER_INSTALL_DIR)/bender: mkdir -p $(BENDER_INSTALL_DIR) && cd $(BENDER_INSTALL_DIR) && \ curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- $(BENDER_VERSION) -.PHONY: all help test-all-presets test-transpose-modes test-config-grid validate-config clean-sim sim-script sim clean-stimuli stimuli bender check-bender +.PHONY: all help test-all-presets test-transpose-modes test-transpose-grid test-cim-grid validate-config clean-sim sim-script sim clean-stimuli stimuli bender check-bender diff --git a/README.md b/README.md index d9880de..b740488 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,8 @@ make test-all-presets make test-transpose-modes # Test configuration parameter combinations (grid testing) -make test-config-grid +make test-transpose-grid +make test-cim-grid # Show available configurations make help @@ -91,6 +92,11 @@ The system includes predefined test configurations: - `rect-tall`: 256×64 matrix (2-element transpose) - `rect-narrow`: 16×128 matrix (1-element transpose) - `rect-elongated`: 128×32 matrix (2-element transpose) +- `copy-small`: 4×4 matrix (copy mode testing) +- `copy-medium`: 64×64 matrix (copy mode testing) +- `cim-small`: 32×128 matrix (CIM mode, 32 inner_dim, 128-bit bandwidth) +- `cim-medium`: 64×256 matrix (CIM mode, 64 inner_dim, 256-bit bandwidth) +- `cim-large`: 128×512 matrix (CIM mode, 64 inner_dim, 512-bit bandwidth) For detailed configuration documentation, see `CONFIG_USAGE.md`. diff --git a/config.mk b/config.mk index fbfa130..48dda83 100644 --- a/config.mk +++ b/config.mk @@ -14,15 +14,19 @@ # Hardware configuration (can be overridden by presets or command line) BANDWIDTH ?= 512 # in bits, multiple of WORD_WIDTH -WORD_WIDTH ?= 32 # in bits, multiple of ELEM_WIDTH +WORD_WIDTH ?= 64 # in bits, multiple of ELEM_WIDTH ELEM_WIDTH ?= 8 # in bits -MEMORY_SIZE ?= 65536 # in words +MEMORY_SIZE ?= 131072 # in words -TRANSP_MODE ?= 1 # 0 = none (copy), 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted +DATAMOVER_MODE ?= 2 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion +TRANSP_MODE ?= 0 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted +CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> A-Layout, 1: row-major -> B-Layout +CIM_INNER_DIM ?= 64 # Inner dimension of the CIM accelerator (in elements): 64 for 64x8 CIM macro +CIM_OUTER_DIM ?= 64 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros # Input matrix dimensions (in elements) -MATRIX_SIZE_M ?= 256 # Matrix height in elements -MATRIX_SIZE_N ?= 256 # Matrix width in elements +MATRIX_SIZE_M ?= 448 # Matrix height in elements +MATRIX_SIZE_N ?= 512 # Matrix width in elements WRITE_BASE_ADDR = $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Element-addressed @@ -32,19 +36,49 @@ NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of # ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (4*32b) # N (bandwidth) consecutive words are read/written in one transaction + +ifeq "$(strip $(DATAMOVER_MODE))" "0" # Copy mode +$(info Copy mode enabled) STIM_READ_BASE_ADDR ?= 0 # Element-addressed STIM_READ_D0_LENGTH ?= $(MATRIX_SIZE_M) # [Nof accesses with bandwidth BW per D0-transfer ("row")] STIM_READ_D0_STRIDE ?= $(MATRIX_SIZE_N) # [Elements] STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # [Number of full D0-transfers ("rows")] STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -> manually compute "next row" stride +STIM_READ_D2_LENGTH ?= 0 # Not used for copy mode +STIM_READ_D2_STRIDE ?= 0 # Not used for copy mode STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] -STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 3'b000 = none (copy), 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem, other values: not accepted -STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH +STIM_TRANSP_MODE ?= 0 +STIM_TRANSP_LEN ?= 0 # ToDo(cdurrer): obsolete? + +STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) +STIM_WRITE_D0_LENGTH ?= $(STIM_READ_D0_LENGTH) +STIM_WRITE_D0_STRIDE ?= $(STIM_READ_D0_STRIDE) +STIM_WRITE_D1_LENGTH ?= $(STIM_READ_D1_LENGTH) +STIM_WRITE_D1_STRIDE ?= $(STIM_READ_D1_STRIDE) +STIM_WRITE_D2_STRIDE ?= 0 +STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) -ifneq "$(strip $(TRANSP_MODE))" "0" +else ifeq "$(strip $(DATAMOVER_MODE))" "1" # Transpose mode $(info Transpose mode $(TRANSP_MODE) enabled) +ifneq ($(filter 1 2 4,$(strip $(TRANSP_MODE))), $(strip $(TRANSP_MODE))) + $(error Invalid TRANSP_MODE $(TRANSP_MODE): must be 1, 2, or 4) +endif + +STIM_READ_BASE_ADDR ?= 0 # Element-addressed +STIM_READ_D0_LENGTH ?= $(MATRIX_SIZE_M) # [Nof accesses with bandwidth BW per D0-transfer ("row")] +STIM_READ_D0_STRIDE ?= $(MATRIX_SIZE_N) # [Elements] +STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # [Number of full D0-transfers ("rows")] +STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -> manually compute "next row" stride +STIM_READ_D2_LENGTH ?= 0 # Not used for read +STIM_READ_D2_STRIDE ?= 0 # Not used for read +STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] + +STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] +STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted +STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH + STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(BANDWIDTH_ELEMS) / $(TRANSP_MODE)))) # Transpose tile width corresponds to bandwidth STIM_WRITE_D0_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(TRANSP_MODE)))) # Transpose: Input matrix height corresponds to output matrix width @@ -52,15 +86,36 @@ STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(STIM_WRITE_D0_STRIDE) / $(BANDWIDTH_E STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # Transpose tile height corresponds to bandwidth STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(BANDWIDTH_ELEMS)))) # D2 length is controlled by total length STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read -else -$(info Transpose mode disabled, using copy mode) -STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) -STIM_WRITE_D0_LENGTH ?= $(STIM_READ_D0_LENGTH) -STIM_WRITE_D0_STRIDE ?= $(STIM_READ_D0_STRIDE) -STIM_WRITE_D1_LENGTH ?= $(STIM_READ_D1_LENGTH) -STIM_WRITE_D1_STRIDE ?= $(STIM_READ_D1_STRIDE) + +else ifeq "$(strip $(DATAMOVER_MODE))" "2" # CIM data layout conversion mode +$(info CIM data layout conversion mode $(CIM_MODE) enabled) +ifneq ($(filter 0 1,$(strip $(CIM_MODE))), $(strip $(CIM_MODE))) + $(error "Invalid CIM_MODE $(CIM_MODE): must be 0 or 1") +endif +STIM_READ_BASE_ADDR ?= 0 # Element-addressed +STIM_READ_D0_LENGTH ?= $(shell echo $$(($(CIM_INNER_DIM) / $(BANDWIDTH_ELEMS)))) # [Nof accesses with bandwidth BW per D0-transfer ("row")] +STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] +STIM_READ_D1_LENGTH ?= $(MATRIX_SIZE_M) +STIM_READ_D1_STRIDE ?= $(MATRIX_SIZE_N) +STIM_READ_D2_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(CIM_INNER_DIM)))) +STIM_READ_D2_STRIDE ?= $(CIM_INNER_DIM) +PARTIAL_MULT = $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) +STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(PARTIAL_MULT) * $(STIM_READ_D2_LENGTH)))) # [Total memory accesses] + +STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] +STIM_TRANSP_MODE ?= 0 +STIM_TRANSP_LEN ?= 0 + +STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed +STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(MATRIX_SIZE_M)))) +STIM_WRITE_D0_STRIDE ?= $(BANDWIDTH_ELEMS) +STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(CIM_INNER_DIM)))) +STIM_WRITE_D1_STRIDE ?= $(shell echo $$(($(STIM_WRITE_D0_LENGTH) * $(BANDWIDTH_ELEMS)))) STIM_WRITE_D2_STRIDE ?= 0 -STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) +STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read + +else +$(error "Invalid DATAMOVER_MODE $(DATAMOVER_MODE): must be 0 (copy), 1 (transpose), or 2 (CIM data layout conversion)") endif # Debug: Print computed values (uncomment to see values during make) @@ -71,7 +126,11 @@ $(info WORD_WIDTH: $(WORD_WIDTH) bits) $(info ELEM_WIDTH: $(ELEM_WIDTH) bits) $(info BANDWIDTH_ELEMS: $(BANDWIDTH_ELEMS)) $(info NUM_ELEM_WORD: $(NUM_ELEM_WORD)) +$(info DATAMOVER_MODE: $(DATAMOVER_MODE)) $(info TRANSP_MODE: $(TRANSP_MODE)) +$(info CIM_MODE: $(CIM_MODE)) +$(info CIM_INNER_DIM: $(CIM_INNER_DIM) elements) +$(info CIM_OUTER_DIM: $(CIM_OUTER_DIM) elements) $(info ) $(info Matrix Configuration:) $(info MATRIX_SIZE: $(MATRIX_SIZE_M) x $(MATRIX_SIZE_N)) @@ -81,6 +140,7 @@ $(info Read Configuration:) $(info BASE_ADDR: $(STIM_READ_BASE_ADDR)) $(info D0_LENGTH: $(STIM_READ_D0_LENGTH), D0_STRIDE: $(STIM_READ_D0_STRIDE)) $(info D1_LENGTH: $(STIM_READ_D1_LENGTH), D1_STRIDE: $(STIM_READ_D1_STRIDE)) +$(info D2_STRIDE: $(STIM_READ_D2_STRIDE)) $(info TOT_LENGTH: $(STIM_READ_TOT_LENGTH)) $(info ) $(info Write Configuration:) diff --git a/config_presets.mk b/config_presets.mk index 312bb4d..7efb568 100644 --- a/config_presets.mk +++ b/config_presets.mk @@ -10,14 +10,19 @@ ######################################### # Available presets: -# - small-matrix : Small 4x4 matrix for quick testing -# - medium-matrix : Medium 64x64 matrix for moderate testing -# - large-matrix : Large 448x448 matrix for stress testing +# - small-matrix : Small 4x4 matrix for quick testing (transpose) +# - medium-matrix : Medium 64x64 matrix for moderate testing (transpose) +# - large-matrix : Large 448x448 matrix for stress testing (transpose) # - transpose-test : Optimized for transpose functionality verification -# - rect-wide : Wide rectangular matrix (64x256) -# - rect-tall : Tall rectangular matrix (256x64) -# - rect-narrow : Narrow rectangular matrix (16x128) -# - rect-elongated : Elongated rectangular matrix (128x32) +# - rect-wide : Wide rectangular matrix (64x256) (transpose) +# - rect-tall : Tall rectangular matrix (256x64) (transpose) +# - rect-narrow : Narrow rectangular matrix (16x128) (transpose) +# - rect-elongated : Elongated rectangular matrix (128x32) (transpose) +# - copy-small : Small 4x4 matrix for copy mode testing +# - copy-medium : Medium 64x64 matrix for copy mode testing +# - cim-small : CIM 32x128 matrix, CIM_INNER_DIM=32, 128-bit bandwidth +# - cim-medium : CIM 64x256 matrix, CIM_INNER_DIM=64, 256-bit bandwidth +# - cim-large : CIM 128x512 matrix, CIM_INNER_DIM=64, 512-bit bandwidth # - custom : User-defined configuration (default) # Select configuration preset (can be overridden via command line) @@ -29,7 +34,11 @@ ifeq ($(CONFIG_PRESET),small-matrix) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 512 + DATAMOVER_MODE = 1 TRANSP_MODE = 1 + CIM_MODE = 0 + CIM_INNER_DIM = 0 + CIM_OUTER_DIM = 0 MATRIX_SIZE_M = 4 MATRIX_SIZE_N = 4 CONFIG_DESC = "Small 4x4 matrix, 1-element transpose" @@ -40,7 +49,11 @@ ifeq ($(CONFIG_PRESET),medium-matrix) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 4096 + DATAMOVER_MODE = 1 TRANSP_MODE = 1 + CIM_MODE = 0 + CIM_INNER_DIM = 0 + CIM_OUTER_DIM = 0 MATRIX_SIZE_M = 64 MATRIX_SIZE_N = 64 CONFIG_DESC = "Medium 64x64 matrix, 1-element transpose" @@ -51,7 +64,11 @@ ifeq ($(CONFIG_PRESET),large-matrix) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 131072 + DATAMOVER_MODE = 1 TRANSP_MODE = 1 + CIM_MODE = 0 + CIM_INNER_DIM = 0 + CIM_OUTER_DIM = 0 MATRIX_SIZE_M = 448 MATRIX_SIZE_N = 448 CONFIG_DESC = "Large 448x448 matrix, 1-element transpose" @@ -62,7 +79,11 @@ ifeq ($(CONFIG_PRESET),transpose-test) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 16384 + DATAMOVER_MODE = 1 TRANSP_MODE = 2 + CIM_MODE = 0 + CIM_INNER_DIM = 0 + CIM_OUTER_DIM = 0 MATRIX_SIZE_M = 32 MATRIX_SIZE_N = 32 CONFIG_DESC = "32x32 matrix, 2-element transpose" @@ -73,7 +94,11 @@ ifeq ($(CONFIG_PRESET),rect-wide) WORD_WIDTH = 16 ELEM_WIDTH = 8 MEMORY_SIZE = 32768 + DATAMOVER_MODE = 1 TRANSP_MODE = 4 + CIM_MODE = 0 + CIM_INNER_DIM = 0 + CIM_OUTER_DIM = 0 MATRIX_SIZE_M = 64 MATRIX_SIZE_N = 256 CONFIG_DESC = "Wide rectangular matrix 64x256, 4-element transpose" @@ -84,7 +109,11 @@ ifeq ($(CONFIG_PRESET),rect-tall) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 32768 + DATAMOVER_MODE = 1 TRANSP_MODE = 2 + CIM_MODE = 0 + CIM_INNER_DIM = 0 + CIM_OUTER_DIM = 0 MATRIX_SIZE_M = 256 MATRIX_SIZE_N = 64 CONFIG_DESC = "Tall rectangular matrix 256x64, 2-element transpose" @@ -95,7 +124,11 @@ ifeq ($(CONFIG_PRESET),rect-narrow) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 8192 + DATAMOVER_MODE = 1 TRANSP_MODE = 1 + CIM_MODE = 0 + CIM_INNER_DIM = 0 + CIM_OUTER_DIM = 0 MATRIX_SIZE_M = 16 MATRIX_SIZE_N = 128 CONFIG_DESC = "Narrow rectangular matrix 16x128, 1-element transpose" @@ -106,12 +139,91 @@ ifeq ($(CONFIG_PRESET),rect-elongated) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 16384 + DATAMOVER_MODE = 1 TRANSP_MODE = 2 + CIM_MODE = 0 + CIM_INNER_DIM = 0 + CIM_OUTER_DIM = 0 MATRIX_SIZE_M = 128 MATRIX_SIZE_N = 32 CONFIG_DESC = "Elongated rectangular matrix 128x32, 2-element transpose" endif +ifeq ($(CONFIG_PRESET),copy-small) + BANDWIDTH = 32 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 512 + DATAMOVER_MODE = 0 + TRANSP_MODE = 0 + CIM_MODE = 0 + CIM_INNER_DIM = 0 + CIM_OUTER_DIM = 0 + MATRIX_SIZE_M = 4 + MATRIX_SIZE_N = 4 + CONFIG_DESC = "Small 4x4 matrix, copy mode" +endif + +ifeq ($(CONFIG_PRESET),copy-medium) + BANDWIDTH = 128 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 4096 + DATAMOVER_MODE = 0 + TRANSP_MODE = 0 + CIM_MODE = 0 + CIM_INNER_DIM = 0 + CIM_OUTER_DIM = 0 + MATRIX_SIZE_M = 64 + MATRIX_SIZE_N = 64 + CONFIG_DESC = "Medium 64x64 matrix, copy mode" +endif + +ifeq ($(CONFIG_PRESET),cim-small) + BANDWIDTH = 128 + WORD_WIDTH = 32 + ELEM_WIDTH = 8 + MEMORY_SIZE = 8192 + DATAMOVER_MODE = 2 + TRANSP_MODE = 0 + CIM_MODE = 0 + CIM_INNER_DIM = 32 + CIM_OUTER_DIM = 16 + MATRIX_SIZE_M = 32 + MATRIX_SIZE_N = 128 + CONFIG_DESC = "CIM 32x128 matrix, CIM_INNER_DIM=32, 128-bit bandwidth" +endif + +ifeq ($(CONFIG_PRESET),cim-medium) + BANDWIDTH = 256 + WORD_WIDTH = 64 + ELEM_WIDTH = 8 + MEMORY_SIZE = 16384 + DATAMOVER_MODE = 2 + TRANSP_MODE = 0 + CIM_MODE = 0 + CIM_INNER_DIM = 64 + CIM_OUTER_DIM = 32 + MATRIX_SIZE_M = 64 + MATRIX_SIZE_N = 256 + CONFIG_DESC = "CIM 64x256 matrix, CIM_INNER_DIM=64, 256-bit bandwidth" +endif + +ifeq ($(CONFIG_PRESET),cim-large) + BANDWIDTH = 512 + WORD_WIDTH = 64 + ELEM_WIDTH = 8 + MEMORY_SIZE = 65536 + DATAMOVER_MODE = 2 + TRANSP_MODE = 0 + CIM_MODE = 0 + CIM_INNER_DIM = 64 + CIM_OUTER_DIM = 64 + MATRIX_SIZE_M = 128 + MATRIX_SIZE_N = 256 + CONFIG_DESC = "CIM 128x512 matrix, CIM_INNER_DIM=64, 512-bit bandwidth" +endif + ifeq ($(CONFIG_PRESET),custom) # Use values from config.mk or command line overrides CONFIG_DESC = "Custom user-defined configuration (config.mk)" diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py index 3de36a1..eb4bfc4 100644 --- a/verif/python/generate_stimuli_test.py +++ b/verif/python/generate_stimuli_test.py @@ -4,30 +4,6 @@ RANDOM_STIMULI = True # If False, counting stimuli are generated in a counting fashion -# OUTPUT_DIR = "generated" - -# BANDWIDTH = 128 # in bits -# WORD_WIDTH = 32 # in bits -# ELEM_WIDTH = 8 # in bits -# MEMORY_SIZE = 512 # in words - -# TRANSP_MODE = 4 # 0 = none, 1 = 1 elem, 2 = 2 elem, 4 = 4 elem - -# NUM_ELEM_WORD = WORD_WIDTH // ELEM_WIDTH # e.g., 4 for 32-bit words with 8-bit elements -# BANDWIDTH_ELEMS = BANDWIDTH // ELEM_WIDTH -# BANDWIDTH_WORDS = BANDWIDTH_ELEMS // NUM_ELEM_WORD - -# MATRIX_SIZE_N = 16 # in elements -# MATRIX_SIZE_M = 4 # in elements - -# READ_BASE_ADDR = 0 # in bytes -# READ_D0_LENGTH = MATRIX_SIZE_N // BANDWIDTH_ELEMS # Nof accesses with bandwidth BW per D0-transfer ("row") -# READ_D1_LENGTH = MATRIX_SIZE_M - -# WRITE_BASE_ADDR = 128 # in bytes -# WRITE_D0_LENGTH = READ_D1_LENGTH -# WRITE_D1_LENGTH = READ_D0_LENGTH - # ToDo(cdurrer): small matrices (N < BW) not working def extract_elements_from_word(word, word_width, elem_width): @@ -109,6 +85,15 @@ def transpose(matrix, size_d0, size_d1, transp_mode): transposed[d0][(d1*transp_mode)+i] = matrix[d1][(d0*transp_mode)+i] return transposed +def cim_layout(matrix, size_n, size_m, cim_mode, cim_inner_dim, cim_outer_dim): + # ToDo(cdurrer): implement CIM mode + cim_matrix = [[0 for _ in range(size_m * cim_inner_dim)] for _ in range(size_n // cim_inner_dim)] + for d2 in range(size_n // cim_inner_dim): + for d1 in range(size_m): + cim_matrix[d2][(d1*cim_inner_dim):(d1*cim_inner_dim+cim_inner_dim)] = matrix[d1][d2*(cim_inner_dim):(d2*cim_inner_dim+cim_inner_dim)] + + return cim_matrix + def main(): # Parse command-line arguments parser = argparse.ArgumentParser(description="Memory Read/Write Simulation with Word-Aligned Strides") @@ -117,6 +102,7 @@ def main(): parser.add_argument("--write_base_addr", type=int, default=0x20, help="Base address for write operations") parser.add_argument("--read_d0_stride", type=int, default=4, help="Stride for d0 read (in bytes)") parser.add_argument("--read_d1_stride", type=int, default=16, help="Stride for d1 read (in bytes)") + parser.add_argument("--read_d2_stride", type=int, default=64, help="Stride for d2 read (in bytes)") parser.add_argument("--read_d0_length", type=int, default=4, help="Length for d0 read") parser.add_argument("--read_d1_length", type=int, default=4, help="Length for d1 read") parser.add_argument("--read_tot_length", type=int, default=16, help="Total read length") @@ -128,8 +114,14 @@ def main(): parser.add_argument("--bandwidth_bits", type=int, default=4, help="Number of bits per transaction") parser.add_argument("--num_elem_word", type=int, default=4, help="Number of elements in a memory bank word") parser.add_argument("--elem_width", type=int, default=8, help="Width of each element (in bits)") + parser.add_argument("--datamover_mode", type=int, default=0, help="Datamover mode (0=normal, 1=CIM)") parser.add_argument("--transp_mode", type=int, default=0, help="Transposition mode (3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem)") parser.add_argument("--transp_len", type=int, default=0, help="Transposition length") + parser.add_argument("--cim_mode", type=int, default=0, help="CIM mode (0=normal, 1=CIM)") + parser.add_argument("--cim_inner_dim", type=int, default=4, help="CIM inner dimension") + parser.add_argument("--cim_outer_dim", type=int, default=4, help="CIM outer dimension") + parser.add_argument("--matrix_size_m", type=int, default=64, help="Matrix height in elements") + parser.add_argument("--matrix_size_n", type=int, default=64, help="Matrix width in elements") parser.add_argument("--output_dir", type=str, default="output", help="Directory for storing output files") args = parser.parse_args() @@ -149,8 +141,8 @@ def main(): TRANSP_MODE = args.transp_mode # MATRIX_SIZE_N = READ_D0_LENGTH * BANDWIDTH_ELEMS # MATRIX_SIZE_M = READ_D1_LENGTH - MATRIX_SIZE_N = READ_D1_LENGTH * BANDWIDTH_ELEMS - MATRIX_SIZE_M = READ_D0_LENGTH + MATRIX_SIZE_N = args.matrix_size_n + MATRIX_SIZE_M = args.matrix_size_m OUTPUT_DIR = args.output_dir @@ -194,14 +186,16 @@ def main(): input_matrix.append(row) # Print input matrix - # print("Input Matrix:") + print("Input Matrix:") # # for i, row in enumerate(input_matrix): # # print(f"Row {i}: {row}") # # print("\n") - # for i, row in enumerate(input_matrix): - # print(f"Row {i}: {[format(elem, 'X') for elem in row]}") + for i, row in enumerate(input_matrix): + print(f"Row {i}: {[format(elem, 'X') for elem in row]}") - if TRANSP_MODE != 0: + if args.datamover_mode == 0: # Copy mode + output_matrix = input_matrix + elif args.datamover_mode == 1: # Transpose mode transposed_matrix = transpose(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, TRANSP_MODE) # print("\nTransposed Matrix:") # # for i, row in enumerate(transposed_matrix): @@ -210,8 +204,17 @@ def main(): # for i, row in enumerate(transposed_matrix): # print(f"Row {i}: {[format(elem, 'X') for elem in row]}") output_matrix = transposed_matrix + + elif args.datamover_mode == 2: # CIM mode + output_matrix = cim_layout(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) else: - output_matrix = input_matrix + raise ValueError("[GM] datamover_mode must be 0 (copy), 1 (transpose), or 2 (CIM).") + + # Print output matrix + print("\nOutput Matrix:") + for i, row in enumerate(output_matrix): + print(f"Row {i}: {[format(elem, 'X') for elem in row]}") + print("\n") # # Convert output matrix back to words output_hex_words = matrix_to_hex_words(output_matrix, ELEM_WIDTH, WORD_WIDTH) diff --git a/verif/python/validate_config.py b/verif/python/validate_config.py index aca2c8e..611bcf7 100755 --- a/verif/python/validate_config.py +++ b/verif/python/validate_config.py @@ -8,11 +8,16 @@ import argparse def validate_config(bandwidth, word_width, elem_width, memory_size, - transp_mode, matrix_m, matrix_n): + datamover_mode, transp_mode, cim_mode, cim_inner_dim, cim_outer_dim, + matrix_size_m, matrix_size_n): """Validate configuration parameters""" errors = [] warnings = [] + # Computed values + bandwidth_elems = bandwidth // elem_width + num_elem_word = word_width // elem_width + # Basic parameter validation if bandwidth % word_width != 0: errors.append(f"BANDWIDTH ({bandwidth}) must be divisible by WORD_WIDTH ({word_width})") @@ -20,33 +25,57 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, if word_width % elem_width != 0: errors.append(f"WORD_WIDTH ({word_width}) must be divisible by ELEM_WIDTH ({elem_width})") + if memory_size < (matrix_size_n * matrix_size_m * elem_width // word_width) * 2: + errors.append(f"MEMORY_SIZE ({memory_size}) is too small for the given matrix size " + f"({matrix_size_m}x{matrix_size_n}) and element width ({elem_width})") + + # Mode validation (based on config.mk) + if datamover_mode not in [0, 1, 2]: + errors.append(f"DATAMOVER_MODE ({datamover_mode}) must be 0 (copy), 1 (transpose), or 2 (CIM data layout conversion)") + if transp_mode not in [0, 1, 2, 4]: errors.append(f"TRANSP_MODE ({transp_mode}) must be 0, 1, 2, or 4") - # Computed values - bandwidth_elems = bandwidth // elem_width - num_elem_word = word_width // elem_width + # Mode consistency validation + if datamover_mode == 0 and transp_mode != 0: + warnings.append(f"Copy mode (DATAMOVER_MODE=0) typically uses TRANSP_MODE=0, but got {transp_mode}") + + if datamover_mode == 1 and transp_mode == 0: + warnings.append(f"Transpose mode (DATAMOVER_MODE=1) typically uses TRANSP_MODE > 0, but got {transp_mode}") + + # CIM-specific validation + if datamover_mode == 2: + if cim_mode not in [0, 1]: + errors.append(f"CIM_MODE ({cim_mode}) must be 0 (row-major -> A-Layout) or 1 (row-major -> B-Layout)") + if cim_inner_dim < bandwidth_elems: + errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) must be greater than elements per bandwidth ({bandwidth_elems})") + if cim_outer_dim < bandwidth_elems: + errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) must be greater than elements per bandwidth ({bandwidth_elems})") + if cim_inner_dim > matrix_size_n: + errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) cannot be greater than matrix width ({matrix_size_n})") + if cim_outer_dim > matrix_size_m: + errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) cannot be greater than matrix height ({matrix_size_m})") # Memory requirements - matrix_elements = matrix_m * matrix_n + matrix_elements = matrix_size_m * matrix_size_n matrix_words = (matrix_elements * elem_width + word_width - 1) // word_width total_memory_needed = matrix_words * 2 # Input + output matrices if total_memory_needed > memory_size: errors.append(f"Memory size ({memory_size} words) insufficient for matrices " - f"({total_memory_needed} words needed for {matrix_m}x{matrix_n} input+output)") + f"({total_memory_needed} words needed for {matrix_size_m}x{matrix_size_n} input+output)") # Matrix dimension alignment errors - if matrix_n % bandwidth_elems != 0: - errors.append(f"Matrix width ({matrix_n}) not aligned to bandwidth " + if matrix_size_n % bandwidth_elems != 0: + errors.append(f"Matrix width ({matrix_size_n}) not aligned to bandwidth " f"({bandwidth_elems} elements)") - if matrix_m % bandwidth_elems != 0: - errors.append(f"Matrix height ({matrix_m}) not aligned to bandwidth " + if matrix_size_m % bandwidth_elems != 0: + errors.append(f"Matrix height ({matrix_size_m}) not aligned to bandwidth " f"({bandwidth_elems} elements)") # Transpose-specific validation - if transp_mode > 0: + if datamover_mode == 1 and transp_mode > 0: if bandwidth_elems % transp_mode != 0: errors.append(f"Bandwidth elements ({bandwidth_elems}) must be divisible " f"by TRANSP_MODE ({transp_mode})") @@ -59,15 +88,21 @@ def main(): parser.add_argument("--word_width", type=int, required=True) parser.add_argument("--elem_width", type=int, required=True) parser.add_argument("--memory_size", type=int, required=True) + parser.add_argument("--datamover_mode", type=int, required=True) parser.add_argument("--transp_mode", type=int, required=True) - parser.add_argument("--matrix_m", type=int, required=True) - parser.add_argument("--matrix_n", type=int, required=True) + parser.add_argument("--cim_mode", type=int, required=True) + parser.add_argument("--cim_inner_dim", type=int, required=True) + parser.add_argument("--cim_outer_dim", type=int, required=True) + parser.add_argument("--matrix_size_m", type=int, required=True) + parser.add_argument("--matrix_size_n", type=int, required=True) args = parser.parse_args() errors, warnings = validate_config( args.bandwidth, args.word_width, args.elem_width, args.memory_size, - args.transp_mode, args.matrix_m, args.matrix_n + args.datamover_mode, args.transp_mode, args.cim_mode, + args.cim_inner_dim, args.cim_outer_dim, + args.matrix_size_m, args.matrix_size_n ) # Print results @@ -87,10 +122,22 @@ def main(): else: print("Configuration validation PASSED!") + # Print mode information + mode_names = {0: "Copy", 1: "Transpose", 2: "CIM Data Layout Conversion"} + cim_mode_names = {0: "row-major -> A-Layout", 1: "row-major -> B-Layout"} + + print(f"\nMode Configuration:") + print(f" DATAMOVER_MODE: {args.datamover_mode} ({mode_names.get(args.datamover_mode, 'Unknown')})") + print(f" TRANSP_MODE: {args.transp_mode}") + print(f" CIM_MODE: {args.cim_mode} ({cim_mode_names.get(args.cim_mode, 'Unknown')})") + if args.datamover_mode == 2: # CIM mode + print(f" CIM_INNER_DIM: {args.cim_inner_dim}") + print(f" CIM_OUTER_DIM: {args.cim_outer_dim}") + # Print computed values bandwidth_elems = args.bandwidth // args.elem_width num_elem_word = args.word_width // args.elem_width - matrix_words = (args.matrix_m * args.matrix_n * args.elem_width + args.word_width - 1) // args.word_width + matrix_words = (args.matrix_size_m * args.matrix_size_n * args.elem_width + args.word_width - 1) // args.word_width print(f"\nComputed values:") print(f" Elements per bandwidth: {bandwidth_elems}") diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index 2208c3e..f359808 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -113,7 +113,7 @@ import tb_package::*; addressgen_t read_addr, write_addr; - assign read_addr = '{`STIM_READ_BASE_ADDR, `STIM_READ_D0_STRIDE, `STIM_READ_D1_STRIDE, '0, `STIM_READ_D0_LENGTH, `STIM_READ_D1_LENGTH, `STIM_READ_TOT_LENGTH}; + assign read_addr = '{`STIM_READ_BASE_ADDR, `STIM_READ_D0_STRIDE, `STIM_READ_D1_STRIDE, `STIM_READ_D2_STRIDE, `STIM_READ_D0_LENGTH, `STIM_READ_D1_LENGTH, `STIM_READ_TOT_LENGTH}; assign write_addr = '{`STIM_WRITE_BASE_ADDR, `STIM_WRITE_D0_STRIDE, `STIM_WRITE_D1_STRIDE, `STIM_WRITE_D2_STRIDE, `STIM_WRITE_D0_LENGTH, `STIM_WRITE_D1_LENGTH, `STIM_WRITE_TOT_LENGTH}; // assign read_addr = '{`STIM_READ_BASE_ADDR, 32'h4, 32'h10, 32'h4, 32'h4, 32'h10}; // assign write_addr = '{32'h40, 32'h4, 32'h10, 32'h4, 32'h4, 32'h10}; @@ -266,7 +266,7 @@ import tb_package::*; periph_write(datamover_package::DATAMOVER_REG_IN_D0_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, read_addr.d0_stride, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_IN_D1_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, read_addr.d1_stride, clk_i, periph_bus); - periph_write(datamover_package::DATAMOVER_REG_IN_D2_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, 32'h0, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_IN_D2_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, read_addr.d2_stride, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_OUT_D0_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.d0_stride, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_OUT_D1_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.d1_stride, clk_i, periph_bus); From 35fcddf445462c392b44e1c634c266609e9965f2 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Thu, 13 Nov 2025 11:29:45 +0100 Subject: [PATCH 19/29] [tb] Improved golden model to handle non-word-aligned matrix sizes --- verif/python/generate_stimuli_test.py | 94 +++++++++++++-------------- verif/python/validate_config.py | 16 ++--- 2 files changed, 53 insertions(+), 57 deletions(-) diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py index eb4bfc4..d89fdc6 100644 --- a/verif/python/generate_stimuli_test.py +++ b/verif/python/generate_stimuli_test.py @@ -1,8 +1,9 @@ import random import argparse import os +import math -RANDOM_STIMULI = True # If False, counting stimuli are generated in a counting fashion +RANDOM_STIMULI = False # If False, counting stimuli are generated in a counting fashion # ToDo(cdurrer): small matrices (N < BW) not working @@ -17,11 +18,17 @@ def extract_elements_from_word(word, word_width, elem_width): elements.append(elem_val) return elements +def convert_memory_to_vector(memory, elem_width, word_width): + """Convert memory (list of hex words) to a flat vector of elements.""" + vector = [] + for word in memory: + elements = extract_elements_from_word(word, word_width, elem_width) + vector.extend(elements) + return vector + def generate_random_hex(size, word_width): """Generate random word_width hex values.""" - def ceildiv(a, b): - return -(a // -b) - hex_length = ceildiv(word_width, 4) # Each hex digit represents 4 bits + hex_length = math.ceil(word_width / 4) # Each hex digit represents 4 bits return [f"{random.randint(0, 2**word_width - 1):0{hex_length}X}" for _ in range(size)] def generate_counting_hex(size, elem_width, word_width): @@ -54,19 +61,11 @@ def matrix_to_hex_words(matrix, elem_width, word_width): """Convert a matrix of elements to a list of hex words.""" elems_per_word = word_width // elem_width hex_words = [] + matrix_flat = sum(matrix, []) - for row in matrix: - # Process each row, grouping elements into words - for i in range(0, len(row), elems_per_word): - elements_for_word = row[i:i + elems_per_word] - - # Pad with zeros if the row doesn't fill a complete word - while len(elements_for_word) < elems_per_word: - elements_for_word.append(0) - - # Pack elements into a word - hex_word = pack_elements_to_word(elements_for_word, elem_width, word_width) - hex_words.append(hex_word) + for i in range(math.ceil(len(matrix_flat) / elems_per_word)): + hex_word = pack_elements_to_word(matrix_flat[i*elems_per_word:i*elems_per_word+elems_per_word], elem_width, word_width) + hex_words.append(hex_word) return hex_words @@ -77,11 +76,12 @@ def write_file(output_dir, filename, content): with open(filepath, "w") as file: file.write("\n".join(content) + "\n") -def transpose(matrix, size_d0, size_d1, transp_mode): - transposed = [[0 for _ in range(size_d1 * transp_mode)] for _ in range(size_d0 // transp_mode)] - for d1 in range(size_d1): - for d0 in range(size_d0 // transp_mode): +def transpose(matrix, size_n, size_m, transp_mode): + transposed = [[0 for _ in range(size_m * transp_mode)] for _ in range(size_n // transp_mode)] + for d1 in range(size_m): + for d0 in range(size_n // transp_mode): for i in range(transp_mode): + # print(f"Transposing element [{d1}][{(d0*transp_mode)+i}] to [{d0}][{(d1*transp_mode)+i}]") transposed[d0][(d1*transp_mode)+i] = matrix[d1][(d0*transp_mode)+i] return transposed @@ -139,29 +139,33 @@ def main(): READ_D1_LENGTH = args.read_d1_length WRITE_BASE_ADDR = args.write_base_addr TRANSP_MODE = args.transp_mode - # MATRIX_SIZE_N = READ_D0_LENGTH * BANDWIDTH_ELEMS - # MATRIX_SIZE_M = READ_D1_LENGTH MATRIX_SIZE_N = args.matrix_size_n MATRIX_SIZE_M = args.matrix_size_m OUTPUT_DIR = args.output_dir + if MEMORY_SIZE < ((MATRIX_SIZE_N * MATRIX_SIZE_M * ELEM_WIDTH // WORD_WIDTH) * 2): + raise ValueError(f"MEMORY_SIZE ({MEMORY_SIZE}) is too small for the given matrix size " + f"({MATRIX_SIZE_M}x{MATRIX_SIZE_N}) and element width ({ELEM_WIDTH})") # num_elem_word must be power of two and greater than zero if args.num_elem_word & (args.num_elem_word - 1) != 0 or args.num_elem_word <= 0: raise ValueError("[GM] num_elem_word must be a power of two and greater than zero.") # bandwidth width must be a multiple of word size if args.bandwidth_bits % WORD_SIZE_BITS != 0: raise ValueError("[GM] bandwidth_bits must be a multiple of the word size (num_elem_word * elem_width).") - # bandwidth width must be a multiple of word size - if ((MATRIX_SIZE_N * ELEM_WIDTH) < args.bandwidth_bits): - raise ValueError("[GM] Matrix width (N) in bits must be at least as large as bandwidth_bits.") - # transp_mode must be valid (0=none, 1=1elem, 2=2elem, 4=4elem) - if args.transp_mode not in [0, 1, 2, 4]: - raise ValueError("[GM] transp_mode must be 0 (none), 1 (1 elem), 2 (2 elem), or 4 (4 elem).") + # # bandwidth width must be a multiple of word size + # if ((MATRIX_SIZE_N * ELEM_WIDTH) < args.bandwidth_bits): + # raise ValueError("[GM] Matrix width (N) in bits must be at least as large as bandwidth_bits.") # read_tot_length must not exceed 12-bit register capacity (4096) - if ((args.read_tot_length >= 4096) & (TRANSP_MODE != 0)): - raise ValueError("[GM] read_tot_length (MxN / BW_ELEM) must be less than 4096 in transpose mode (12-bit register limit).") + if ((args.read_tot_length >= 4096) & (args.datamover_mode != 0)): + raise ValueError("[GM] read_tot_length (MxN / BW_ELEM) must be less than 4096 in transpose and CIM modes (12-bit register limit).") + if(args.datamover_mode == 1): # transpose mode + # transp_mode must be valid (1=1elem, 2=2elem, 4=4elem) ToDo(cdurrer): update with datamover_mode etc + if args.transp_mode not in [1, 2, 4]: + raise ValueError("[GM] transp_mode must be 1 (1 elem), 2 (2 elem), or 4 (4 elem).") + if (MATRIX_SIZE_N % args.transp_mode) != 0: + raise ValueError(f"[GM] Matrix width N ({MATRIX_SIZE_N}) must be a multiple of transp_mode ({args.transp_mode}).") print(f"Memory Size: {MEMORY_SIZE} entries") print(f"Word Size: {WORD_SIZE_BITS} bits") @@ -174,16 +178,16 @@ def main(): write_file(OUTPUT_DIR, "initial_memory.txt", memory) + # Convert memory to flat vector + memory_flat = convert_memory_to_vector(memory, ELEM_WIDTH, WORD_WIDTH) + print(memory_flat) + # Extract matrix (read dimensions) from memory - input_matrix = [] + input_matrix = [[0 for _ in range(MATRIX_SIZE_N)] for _ in range(MATRIX_SIZE_M)] for d1 in range(MATRIX_SIZE_M): row = [] - for d0 in range(MATRIX_SIZE_N // NUM_ELEM_WORD): - word = memory[(READ_BASE_ADDR // (WORD_WIDTH // 8) + d1 * (MATRIX_SIZE_N // NUM_ELEM_WORD) + d0)] - word_elements = extract_elements_from_word(word, NUM_ELEM_WORD*ELEM_WIDTH, ELEM_WIDTH) - for elem in range(NUM_ELEM_WORD): - row.append(word_elements[elem]) - input_matrix.append(row) + for d0 in range(MATRIX_SIZE_N): + input_matrix[d1][d0] = memory_flat[(READ_BASE_ADDR + d1 * MATRIX_SIZE_N + d0)] # Print input matrix print("Input Matrix:") @@ -196,15 +200,7 @@ def main(): if args.datamover_mode == 0: # Copy mode output_matrix = input_matrix elif args.datamover_mode == 1: # Transpose mode - transposed_matrix = transpose(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, TRANSP_MODE) - # print("\nTransposed Matrix:") - # # for i, row in enumerate(transposed_matrix): - # # print(f"Row {i}: {row}") - # # print("\n") - # for i, row in enumerate(transposed_matrix): - # print(f"Row {i}: {[format(elem, 'X') for elem in row]}") - output_matrix = transposed_matrix - + output_matrix = transpose(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, TRANSP_MODE) elif args.datamover_mode == 2: # CIM mode output_matrix = cim_layout(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) else: @@ -218,9 +214,9 @@ def main(): # # Convert output matrix back to words output_hex_words = matrix_to_hex_words(output_matrix, ELEM_WIDTH, WORD_WIDTH) - # print(f"\nOutput Matrix as Hex Words:") - # for i, word in enumerate(output_hex_words): - # print(f"Word {i}: {word}") + print(f"\nOutput Matrix as Hex Words:") + for i, word in enumerate(output_hex_words): + print(f"Word {i}: {word}") # Write back output matrix to memory at WRITE_BASE_ADDR for i, word in enumerate(output_hex_words): diff --git a/verif/python/validate_config.py b/verif/python/validate_config.py index 611bcf7..ebdc47e 100755 --- a/verif/python/validate_config.py +++ b/verif/python/validate_config.py @@ -65,14 +65,14 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, errors.append(f"Memory size ({memory_size} words) insufficient for matrices " f"({total_memory_needed} words needed for {matrix_size_m}x{matrix_size_n} input+output)") - # Matrix dimension alignment errors - if matrix_size_n % bandwidth_elems != 0: - errors.append(f"Matrix width ({matrix_size_n}) not aligned to bandwidth " - f"({bandwidth_elems} elements)") - - if matrix_size_m % bandwidth_elems != 0: - errors.append(f"Matrix height ({matrix_size_m}) not aligned to bandwidth " - f"({bandwidth_elems} elements)") + # # Matrix dimension alignment errors + # if matrix_size_n % bandwidth_elems != 0: + # errors.append(f"Matrix width ({matrix_size_n}) not aligned to bandwidth " + # f"({bandwidth_elems} elements)") + + # if matrix_size_m % bandwidth_elems != 0: + # errors.append(f"Matrix height ({matrix_size_m}) not aligned to bandwidth " + # f"({bandwidth_elems} elements)") # Transpose-specific validation if datamover_mode == 1 and transp_mode > 0: From bedb4802c80f1cbb8a4f005bf18fb976650e1aff Mon Sep 17 00:00:00 2001 From: cdurrer Date: Mon, 17 Nov 2025 10:25:47 +0100 Subject: [PATCH 20/29] [tb] Introduced MISALIGNED_ACCESSES parameter, switched copy mode order (d0/d1) --- Makefile | 7 ++-- README.md | 2 ++ ReadMe.md | 46 --------------------------- config.mk | 33 +++++++++++-------- config_presets.mk | 13 ++++++++ verif/python/generate_stimuli_test.py | 42 +++++++++++++++++------- verif/python/validate_config.py | 1 + verif/tb/tb_datamover_top_wrap.sv | 2 +- verif/tb/tb_package.sv | 1 + 9 files changed, 72 insertions(+), 75 deletions(-) delete mode 100644 ReadMe.md diff --git a/Makefile b/Makefile index 8382836..e16c60b 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,7 @@ TESTBENCH_DEFINES += -DSTIM_TRANSP_LEN=${STIM_TRANSP_LEN} TESTBENCH_DEFINES += -DBANDWIDTH=${BANDWIDTH} TESTBENCH_DEFINES += -DNUM_ELEM_WORD=${NUM_ELEM_WORD} TESTBENCH_DEFINES += -DELEM_WIDTH=${ELEM_WIDTH} +TESTBENCH_DEFINES += -DMISALIGNED_ACCESSES=${MISALIGNED_ACCESSES} # .PHONY: clean-sim sim-script sim synopsys-script @@ -125,7 +126,7 @@ test-transpose-modes: @failed_tests=""; \ for mode in 1 2 4; do \ echo "=== Testing TRANSP_MODE=$$mode ==="; \ - if $(MAKE) sim CONFIG_PRESET=transpose-test TRANSP_MODE=$$mode; then \ + if $(MAKE) sim CONFIG_PRESET=transpose-test DATAMOVER_MODE=1 TRANSP_MODE=$$mode; then \ echo "✓ TRANSP_MODE=$$mode: PASSED"; \ else \ echo "✗ TRANSP_MODE=$$mode: FAILED"; \ @@ -153,7 +154,7 @@ test-transpose-grid: total_tests=$$((total_tests + 1)); \ echo "=== Testing BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width ==="; \ if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ - echo "✓ BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: PASSED"; \ + echo "✓ BANDWIDTH=$$bandwidth DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: PASSED"; \ passed_tests=$$((passed_tests + 1)); \ else \ echo "✗ BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: FAILED"; \ @@ -240,6 +241,7 @@ validate-config: --word_width $(WORD_WIDTH) \ --elem_width $(ELEM_WIDTH) \ --memory_size $(MEMORY_SIZE) \ + --misaligned_accesses $(MISALIGNED_ACCESSES) \ --datamover_mode $(DATAMOVER_MODE) \ --transp_mode $(TRANSP_MODE) \ --cim_mode $(CIM_MODE) \ @@ -286,6 +288,7 @@ stimuli: clean-stimuli --bandwidth_bits $(BANDWIDTH) \ --num_elem_word $(NUM_ELEM_WORD) \ --elem_width $(ELEM_WIDTH) \ + --misaligned_accesses $(MISALIGNED_ACCESSES) \ --datamover_mode $(DATAMOVER_MODE) \ --transp_mode $(STIM_TRANSP_MODE) \ --transp_len $(STIM_TRANSP_LEN) \ diff --git a/README.md b/README.md index b740488..3a47c33 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,8 @@ PASSED!!!! ## Contributors - Francesco Conti, University of Bologna (*f.conti@unibo.it*) - Arpan Suravi Prasad, ETH Zurich (*prasadar@iis.ee.ethz.ch*) +- Sergio Mazzola, ETH Zurich (*smazzola@iis.ee.ethz.ch*) +- Cyrill Durrer, ETH Zurich (*cdurrer@iis.ee.ethz.ch*) ## License This repository makes use of two licenses: diff --git a/ReadMe.md b/ReadMe.md deleted file mode 100644 index eae3f30..0000000 --- a/ReadMe.md +++ /dev/null @@ -1,46 +0,0 @@ -# Project Build and Simulation Guide - -This ReadMe provides instructions on how to set up and run the standalone simulation using `make` commands. - -## Available Make Commands - -### 1. To clone the dependencies, run: -```sh -make bender -``` - -### 2. Generate Stimuli and Golden Files -To generate the stimuli and golden reference using a Python script, run: -```sh -make stimuli -``` - -### 3. Create Compilation Script -To create a compilation script for compiling the hardware, run: -```sh -make sim-script -``` - -### 4. Simulate the Design -To simulate the RTL, execute: -```sh -make sim -``` -By default QuestaSim GUI is active. You can simulate the RTL in CLI mode with `GUI=0 make sim`. - -## Test Results -If the tests pass successfully, you should see the following message displayed at the end: -``` -PASSED!!!! -``` - -## Contributors -- Francesco Conti, University of Bologna (*f.conti@unibo.it*) -- Arpan Suravi Prasad, ETH Zurich (*prasadar@iis.ee.ethz.ch*) - -## License -This repository makes use of two licenses: -- for all *software*: Apache License Version 2.0 -- for all *hardware*: Solderpad Hardware License Version 0.51 - -For further information have a look at the license files: `LICENSE.hw`, `LICENSE.sw` diff --git a/config.mk b/config.mk index 48dda83..ce166e3 100644 --- a/config.mk +++ b/config.mk @@ -13,25 +13,28 @@ ####################### # Hardware configuration (can be overridden by presets or command line) -BANDWIDTH ?= 512 # in bits, multiple of WORD_WIDTH -WORD_WIDTH ?= 64 # in bits, multiple of ELEM_WIDTH +BANDWIDTH ?= 64 # in bits, multiple of WORD_WIDTH +WORD_WIDTH ?= 32 # in bits, multiple of ELEM_WIDTH ELEM_WIDTH ?= 8 # in bits -MEMORY_SIZE ?= 131072 # in words +MEMORY_SIZE ?= 512 # in words +MISALIGNED_ACCESSES ?= 1 -DATAMOVER_MODE ?= 2 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion -TRANSP_MODE ?= 0 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted +DATAMOVER_MODE ?= 0 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion +TRANSP_MODE ?= 4 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> A-Layout, 1: row-major -> B-Layout -CIM_INNER_DIM ?= 64 # Inner dimension of the CIM accelerator (in elements): 64 for 64x8 CIM macro -CIM_OUTER_DIM ?= 64 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros +CIM_INNER_DIM ?= 32 # Inner dimension of the CIM accelerator (in elements): 64 for 64x8 CIM macro +CIM_OUTER_DIM ?= 32 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros # Input matrix dimensions (in elements) -MATRIX_SIZE_M ?= 448 # Matrix height in elements -MATRIX_SIZE_N ?= 512 # Matrix width in elements +MATRIX_SIZE_M ?= 8 # Matrix height in elements +MATRIX_SIZE_N ?= 8 # Matrix width in elements WRITE_BASE_ADDR = $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Element-addressed # Derived constants from basic parameters -BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH) / $(ELEM_WIDTH)))) # Number of elements per bandwidth +BANDWIDTH_REDUCTION := $(shell echo $$(($(MISALIGNED_ACCESSES) * $(WORD_WIDTH)))) # in bits +BANDWIDTH_ALIGNED := $(shell echo $$(($(BANDWIDTH) - $(BANDWIDTH_REDUCTION)))) # in bytes +BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH_ALIGNED) / $(ELEM_WIDTH)))) # Number of elements per bandwidth NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of elements per word # ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (4*32b) @@ -40,10 +43,10 @@ NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of ifeq "$(strip $(DATAMOVER_MODE))" "0" # Copy mode $(info Copy mode enabled) STIM_READ_BASE_ADDR ?= 0 # Element-addressed -STIM_READ_D0_LENGTH ?= $(MATRIX_SIZE_M) # [Nof accesses with bandwidth BW per D0-transfer ("row")] -STIM_READ_D0_STRIDE ?= $(MATRIX_SIZE_N) # [Elements] -STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # [Number of full D0-transfers ("rows")] -STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -> manually compute "next row" stride +STIM_READ_D0_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # [Nof accesses with bandwidth BW per D0-transfer ("row")] +STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] +STIM_READ_D1_LENGTH ?= $(MATRIX_SIZE_M) # [Number of full D0-transfers ("rows")] +STIM_READ_D1_STRIDE ?= $(MATRIX_SIZE_N) # [Elements] -> manually compute "next row" stride STIM_READ_D2_LENGTH ?= 0 # Not used for copy mode STIM_READ_D2_STRIDE ?= 0 # Not used for copy mode STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] @@ -122,6 +125,8 @@ endif $(info ========================================) $(info Hardware Configuration:) $(info BANDWIDTH: $(BANDWIDTH) bits) +$(info MISALIGNED_ACCESSES: $(MISALIGNED_ACCESSES)) +$(info BANDWIDTH_ALIGNED: $(BANDWIDTH_ALIGNED) bits) $(info WORD_WIDTH: $(WORD_WIDTH) bits) $(info ELEM_WIDTH: $(ELEM_WIDTH) bits) $(info BANDWIDTH_ELEMS: $(BANDWIDTH_ELEMS)) diff --git a/config_presets.mk b/config_presets.mk index 7efb568..d3a518b 100644 --- a/config_presets.mk +++ b/config_presets.mk @@ -34,6 +34,7 @@ ifeq ($(CONFIG_PRESET),small-matrix) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 512 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 1 TRANSP_MODE = 1 CIM_MODE = 0 @@ -49,6 +50,7 @@ ifeq ($(CONFIG_PRESET),medium-matrix) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 4096 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 1 TRANSP_MODE = 1 CIM_MODE = 0 @@ -64,6 +66,7 @@ ifeq ($(CONFIG_PRESET),large-matrix) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 131072 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 1 TRANSP_MODE = 1 CIM_MODE = 0 @@ -79,6 +82,7 @@ ifeq ($(CONFIG_PRESET),transpose-test) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 16384 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 1 TRANSP_MODE = 2 CIM_MODE = 0 @@ -94,6 +98,7 @@ ifeq ($(CONFIG_PRESET),rect-wide) WORD_WIDTH = 16 ELEM_WIDTH = 8 MEMORY_SIZE = 32768 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 1 TRANSP_MODE = 4 CIM_MODE = 0 @@ -109,6 +114,7 @@ ifeq ($(CONFIG_PRESET),rect-tall) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 32768 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 1 TRANSP_MODE = 2 CIM_MODE = 0 @@ -124,6 +130,7 @@ ifeq ($(CONFIG_PRESET),rect-narrow) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 8192 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 1 TRANSP_MODE = 1 CIM_MODE = 0 @@ -139,6 +146,7 @@ ifeq ($(CONFIG_PRESET),rect-elongated) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 16384 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 1 TRANSP_MODE = 2 CIM_MODE = 0 @@ -154,6 +162,7 @@ ifeq ($(CONFIG_PRESET),copy-small) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 512 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 0 TRANSP_MODE = 0 CIM_MODE = 0 @@ -169,6 +178,7 @@ ifeq ($(CONFIG_PRESET),copy-medium) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 4096 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 0 TRANSP_MODE = 0 CIM_MODE = 0 @@ -184,6 +194,7 @@ ifeq ($(CONFIG_PRESET),cim-small) WORD_WIDTH = 32 ELEM_WIDTH = 8 MEMORY_SIZE = 8192 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 2 TRANSP_MODE = 0 CIM_MODE = 0 @@ -199,6 +210,7 @@ ifeq ($(CONFIG_PRESET),cim-medium) WORD_WIDTH = 64 ELEM_WIDTH = 8 MEMORY_SIZE = 16384 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 2 TRANSP_MODE = 0 CIM_MODE = 0 @@ -214,6 +226,7 @@ ifeq ($(CONFIG_PRESET),cim-large) WORD_WIDTH = 64 ELEM_WIDTH = 8 MEMORY_SIZE = 65536 + MISALIGNED_ACCESSES = 0 DATAMOVER_MODE = 2 TRANSP_MODE = 0 CIM_MODE = 0 diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py index d89fdc6..2c255a9 100644 --- a/verif/python/generate_stimuli_test.py +++ b/verif/python/generate_stimuli_test.py @@ -4,13 +4,13 @@ import math RANDOM_STIMULI = False # If False, counting stimuli are generated in a counting fashion +WORD_ALIGNED = False # If True, matrices are aligned to word boundaries by zero-padding # ToDo(cdurrer): small matrices (N < BW) not working def extract_elements_from_word(word, word_width, elem_width): """Extract elements from a word based on the specified widths.""" word_int = int(word, 16) # Convert hex string to integer - elements = [] for i in range(word_width // elem_width): # Extract each element using shift and mask @@ -44,17 +44,14 @@ def generate_counting_hex(size, elem_width, word_width): elem_val = (i * elems_per_word + j) & ((1 << elem_width) - 1) word_val |= (elem_val << (j * elem_width)) result.append(f"{word_val:0{word_width // 4}X}") # Format as hex string - return result def pack_elements_to_word(elements, elem_width, word_width): """Pack multiple elements into a single word.""" elems_per_word = word_width // elem_width word_val = 0 - for i, elem in enumerate(elements[:elems_per_word]): # Take only what fits in a word word_val |= (elem << (i * elem_width)) - return f"{word_val:0{word_width // 4}X}" def matrix_to_hex_words(matrix, elem_width, word_width): @@ -62,11 +59,27 @@ def matrix_to_hex_words(matrix, elem_width, word_width): elems_per_word = word_width // elem_width hex_words = [] matrix_flat = sum(matrix, []) - for i in range(math.ceil(len(matrix_flat) / elems_per_word)): hex_word = pack_elements_to_word(matrix_flat[i*elems_per_word:i*elems_per_word+elems_per_word], elem_width, word_width) hex_words.append(hex_word) + return hex_words +def matrix_to_hex_words_word_aligned(matrix, elem_width, word_width): + """Convert a matrix of elements to a list of hex words - aligned to word boundaries by zero-padding.""" + elems_per_word = word_width // elem_width + hex_words = [] + for row in matrix: + # Process each row, grouping elements into words + for i in range(0, len(row), elems_per_word): + elements_for_word = row[i:i + elems_per_word] + + # Pad with zeros if the row doesn't fill a complete word + while len(elements_for_word) < elems_per_word: + elements_for_word.append(0) + + # Pack elements into a word + hex_word = pack_elements_to_word(elements_for_word, elem_width, word_width) + hex_words.append(hex_word) return hex_words def write_file(output_dir, filename, content): @@ -114,6 +127,7 @@ def main(): parser.add_argument("--bandwidth_bits", type=int, default=4, help="Number of bits per transaction") parser.add_argument("--num_elem_word", type=int, default=4, help="Number of elements in a memory bank word") parser.add_argument("--elem_width", type=int, default=8, help="Width of each element (in bits)") + parser.add_argument("--misaligned_accesses", type=int, default=0, help="Enable misaligned accesses (0=disabled, 1=enabled)") parser.add_argument("--datamover_mode", type=int, default=0, help="Datamover mode (0=normal, 1=CIM)") parser.add_argument("--transp_mode", type=int, default=0, help="Transposition mode (3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem)") parser.add_argument("--transp_len", type=int, default=0, help="Transposition length") @@ -126,8 +140,9 @@ def main(): args = parser.parse_args() + BANDWIDTH_ALIGNED = args.bandwidth_bits - (args.misaligned_accesses * (args.elem_width * args.num_elem_word)) MEMORY_SIZE = args.mem_size # Set global memory size - BANDWIDTH_ELEMS = args.bandwidth_bits // args.elem_width + BANDWIDTH_ELEMS = BANDWIDTH_ALIGNED // args.elem_width BANDWIDTH_WORDS = BANDWIDTH_ELEMS // args.num_elem_word WORD_SIZE_BITS = args.num_elem_word * args.elem_width # Set global word size in bits @@ -151,11 +166,11 @@ def main(): if args.num_elem_word & (args.num_elem_word - 1) != 0 or args.num_elem_word <= 0: raise ValueError("[GM] num_elem_word must be a power of two and greater than zero.") # bandwidth width must be a multiple of word size - if args.bandwidth_bits % WORD_SIZE_BITS != 0: - raise ValueError("[GM] bandwidth_bits must be a multiple of the word size (num_elem_word * elem_width).") + if BANDWIDTH_ALIGNED % WORD_SIZE_BITS != 0: + raise ValueError("[GM] BANDWIDTH_ALIGNED must be a multiple of the word size (num_elem_word * elem_width).") # # bandwidth width must be a multiple of word size - # if ((MATRIX_SIZE_N * ELEM_WIDTH) < args.bandwidth_bits): - # raise ValueError("[GM] Matrix width (N) in bits must be at least as large as bandwidth_bits.") + # if ((MATRIX_SIZE_N * ELEM_WIDTH) < BANDWIDTH_ALIGNED): + # raise ValueError("[GM] Matrix width (N) in bits must be at least as large as BANDWIDTH_ALIGNED.") # read_tot_length must not exceed 12-bit register capacity (4096) if ((args.read_tot_length >= 4096) & (args.datamover_mode != 0)): raise ValueError("[GM] read_tot_length (MxN / BW_ELEM) must be less than 4096 in transpose and CIM modes (12-bit register limit).") @@ -180,7 +195,6 @@ def main(): # Convert memory to flat vector memory_flat = convert_memory_to_vector(memory, ELEM_WIDTH, WORD_WIDTH) - print(memory_flat) # Extract matrix (read dimensions) from memory input_matrix = [[0 for _ in range(MATRIX_SIZE_N)] for _ in range(MATRIX_SIZE_M)] @@ -213,7 +227,11 @@ def main(): print("\n") # # Convert output matrix back to words - output_hex_words = matrix_to_hex_words(output_matrix, ELEM_WIDTH, WORD_WIDTH) + if (WORD_ALIGNED): + output_hex_words = matrix_to_hex_words_word_aligned(output_matrix, ELEM_WIDTH, WORD_WIDTH) + else: + output_hex_words = matrix_to_hex_words(output_matrix, ELEM_WIDTH, WORD_WIDTH) + print(f"\nOutput Matrix as Hex Words:") for i, word in enumerate(output_hex_words): print(f"Word {i}: {word}") diff --git a/verif/python/validate_config.py b/verif/python/validate_config.py index ebdc47e..f69b657 100755 --- a/verif/python/validate_config.py +++ b/verif/python/validate_config.py @@ -88,6 +88,7 @@ def main(): parser.add_argument("--word_width", type=int, required=True) parser.add_argument("--elem_width", type=int, required=True) parser.add_argument("--memory_size", type=int, required=True) + parser.add_argument("--misaligned_accesses", type=int, required=True) parser.add_argument("--datamover_mode", type=int, required=True) parser.add_argument("--transp_mode", type=int, required=True) parser.add_argument("--cim_mode", type=int, required=True) diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index f359808..096477c 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -138,7 +138,7 @@ import tb_package::*; .ELEM_WIDTH ( ELEM_WIDTH ), .N_CORES ( N_CORES ), .N_CONTEXT ( 2 ), - .MISALIGNED_ACCESSES ( 0 ) + .MISALIGNED_ACCESSES ( MISALIGNED_ACCESSES ) ) i_hwpe_top_wrap ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), diff --git a/verif/tb/tb_package.sv b/verif/tb/tb_package.sv index a802fac..61f6038 100644 --- a/verif/tb/tb_package.sv +++ b/verif/tb/tb_package.sv @@ -32,6 +32,7 @@ package tb_package; parameter BANDWIDTH = `BANDWIDTH; parameter NUM_ELEM_WORD = `NUM_ELEM_WORD; parameter ELEM_WIDTH = `ELEM_WIDTH; + parameter MISALIGNED_ACCESSES = `MISALIGNED_ACCESSES; parameter logic [2:0] TRANSP_MODE = `STIM_TRANSP_MODE; From 2497da931c10cc96c999dfa520a27ed568d77079 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Thu, 20 Nov 2025 09:44:55 +0100 Subject: [PATCH 21/29] [rtl,tb] Started implementing misaligned access for non-word-aligned matrices --- Makefile | 53 ++++++++++++++++++++++++++- config.mk | 19 +++++----- rtl/datamover_engine.sv | 17 ++++++++- rtl/datamover_top.sv | 2 +- verif/python/generate_stimuli_test.py | 5 +++ 5 files changed, 83 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index e16c60b..bdbf7ab 100644 --- a/Makefile +++ b/Makefile @@ -153,8 +153,8 @@ test-transpose-grid: for word_width in 16 32 64; do \ total_tests=$$((total_tests + 1)); \ echo "=== Testing BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width ==="; \ - if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ - echo "✓ BANDWIDTH=$$bandwidth DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: PASSED"; \ + if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ + echo "✓ BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: PASSED"; \ passed_tests=$$((passed_tests + 1)); \ else \ echo "✗ BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: FAILED"; \ @@ -177,6 +177,55 @@ test-transpose-grid: echo "====== SUMMARY: All bandwidth/transpose/word width combinations PASSED! ======"; \ fi +test-transpose-grid-misaligned: + @echo "Testing configuration parameter combinations (grid)..." + @failed_tests=""; \ + total_tests=0; \ + passed_tests=0; \ + for bandwidth in 160 288; do \ + for transp_mode in 1 2 4; do \ + for word_width in 32; do \ + total_tests=$$((total_tests + 1)); \ + echo "=== Testing BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width ==="; \ + if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ + echo "✓ BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: PASSED"; \ + passed_tests=$$((passed_tests + 1)); \ + else \ + echo "✗ BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: FAILED"; \ + failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/MISALIGNED_ACCESSES=1/TRANSP_MODE=$$transp_mode/WORD_WIDTH=$$word_width"; \ + fi; \ + done; \ + done; \ + done; \ + for bandwidth in 320 576; do \ + for transp_mode in 1 2 4; do \ + for word_width in 64; do \ + total_tests=$$((total_tests + 1)); \ + echo "=== Testing BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width ==="; \ + if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ + echo "✓ BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: PASSED"; \ + passed_tests=$$((passed_tests + 1)); \ + else \ + echo "✗ BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: FAILED"; \ + failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/MISALIGNED_ACCESSES=1/TRANSP_MODE=$$transp_mode/WORD_WIDTH=$$word_width"; \ + fi; \ + done; \ + done; \ + done; \ + echo ""; \ + echo "====== TEST SUMMARY ======"; \ + echo "Total tests: $$total_tests"; \ + echo "Passed: $$passed_tests"; \ + echo "Failed: $$((total_tests - passed_tests))"; \ + if [ -n "$$failed_tests" ]; then \ + echo ""; \ + echo "FAILED combinations:$$failed_tests"; \ + exit 1; \ + else \ + echo ""; \ + echo "====== SUMMARY: All bandwidth/transpose/word width combinations PASSED! ======"; \ + fi + test-cim-grid: @echo "Testing CIM configuration parameter combinations (grid)..." @failed_tests=""; \ diff --git a/config.mk b/config.mk index ce166e3..e532895 100644 --- a/config.mk +++ b/config.mk @@ -20,8 +20,8 @@ MEMORY_SIZE ?= 512 # in words MISALIGNED_ACCESSES ?= 1 DATAMOVER_MODE ?= 0 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion -TRANSP_MODE ?= 4 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted -CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> A-Layout, 1: row-major -> B-Layout +TRANSP_MODE ?= 1 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted +CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> A-Layout, 1: row-major -> B-Layout CIM_INNER_DIM ?= 32 # Inner dimension of the CIM accelerator (in elements): 64 for 64x8 CIM macro CIM_OUTER_DIM ?= 32 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros @@ -36,6 +36,7 @@ BANDWIDTH_REDUCTION := $(shell echo $$(($(MISALIGNED_ACCESSES) * $(WORD_WIDTH))) BANDWIDTH_ALIGNED := $(shell echo $$(($(BANDWIDTH) - $(BANDWIDTH_REDUCTION)))) # in bytes BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH_ALIGNED) / $(ELEM_WIDTH)))) # Number of elements per bandwidth NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of elements per word +MATRIX_SIZE_TOT := $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Total number of elements in the matrix # ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (4*32b) # N (bandwidth) consecutive words are read/written in one transaction @@ -43,17 +44,17 @@ NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of ifeq "$(strip $(DATAMOVER_MODE))" "0" # Copy mode $(info Copy mode enabled) STIM_READ_BASE_ADDR ?= 0 # Element-addressed -STIM_READ_D0_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # [Nof accesses with bandwidth BW per D0-transfer ("row")] -STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -STIM_READ_D1_LENGTH ?= $(MATRIX_SIZE_M) # [Number of full D0-transfers ("rows")] -STIM_READ_D1_STRIDE ?= $(MATRIX_SIZE_N) # [Elements] -> manually compute "next row" stride +STIM_READ_D0_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS)))) # [Nof accesses with bandwidth BW per D0-transfer ("row")] ToDo(cdurrer): not working for misaligned matrices +STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] +STIM_READ_D1_LENGTH ?= 0 # [Number of full D0-transfers ("rows")] +STIM_READ_D1_STRIDE ?= 0 # [Elements] -> manually compute "next row" stride STIM_READ_D2_LENGTH ?= 0 # Not used for copy mode STIM_READ_D2_STRIDE ?= 0 # Not used for copy mode -STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] +STIM_READ_TOT_LENGTH ?= $(MATRIX_SIZE_TOT) # [Total memory accesses] -STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] +STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= 0 -STIM_TRANSP_LEN ?= 0 # ToDo(cdurrer): obsolete? +STIM_TRANSP_LEN ?= 0 STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) STIM_WRITE_D0_LENGTH ?= $(STIM_READ_D0_LENGTH) diff --git a/rtl/datamover_engine.sv b/rtl/datamover_engine.sv index a6cfd78..3c2ed99 100644 --- a/rtl/datamover_engine.sv +++ b/rtl/datamover_engine.sv @@ -16,6 +16,11 @@ * Sergio Mazzola */ +// ToDo(cdurrer): TEMP +parameter int unsigned MATRIX_SIZE_TOT = 25; +parameter int unsigned REMAINING_ELEMS = 1; + + module datamover_engine import hwpe_stream_package::*; import hci_package::*; @@ -37,6 +42,8 @@ module datamover_engine input logic clear_i, // FIXME make it ctrl input ctrl_engine_t ctrl_i, + // input logic [31:0] matrix_size_m_i, + // input logic [31:0] matrix_size_n_i, // input data stream + handshake hwpe_stream_intf_stream.sink data_in, // output data stream + handshake @@ -51,6 +58,7 @@ module datamover_engine datamover_engine_fsm_t fsm_d, fsm_q; logic clear_elem_matrix; logic [$clog2(NB_ELEMENTS):0] cnt_q, cnt_d; + logic [11:0] tot_cnt_q, tot_cnt_d; // ToDo(cdurrer): bitwidth? logic cnt_en; logic [NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] data_in_unrolled; logic data_in_valid; @@ -136,7 +144,9 @@ module datamover_engine .push_i ( data_out_prefifo ), .pop_o ( data_out ) ); - assign data_out_prefifo.strb = '1; // FIXME for leftovers + // assign data_out_prefifo.strb = '1; // FIXME for leftovers ToDo(cdurrer): use for partial tiles + assign data_out_prefifo.strb = (tot_cnt_q >= 7-1) ? (1 << REMAINING_ELEMS) - 1 : '1; + assign data_out_prefifo.data = data_out_unrolled; assign data_out_prefifo.valid = data_out_valid; assign data_out_ready = data_out_prefifo.ready; @@ -146,17 +156,22 @@ module datamover_engine begin if (~rst_ni) begin cnt_q <= '0; + tot_cnt_q <= '0; end else if(clear_i) begin cnt_q <= '0; + tot_cnt_q <= '0; end else if(cnt_en) begin cnt_q <= cnt_d; + tot_cnt_q <= tot_cnt_d; end end assign cnt_d = cnt_q < ctrl_i.transp_len-ctrl_i.transp_stride ? cnt_q+ctrl_i.transp_stride : '0; assign cnt_en = fsm_q == WRITE ? data_in_valid & data_in_ready : data_out_valid & data_out_ready; + assign tot_cnt_d = (tot_cnt_q < MATRIX_SIZE_TOT) && (data_out_prefifo.valid) ? tot_cnt_q + 1 : tot_cnt_q; + // "Smart shifting": this set of combinational blocks shifts data_in_unrolled // appropriately, depending on the configuration. // E.g., if you have a classical configuration with diff --git a/rtl/datamover_top.sv b/rtl/datamover_top.sv index 9edd810..9863b85 100644 --- a/rtl/datamover_top.sv +++ b/rtl/datamover_top.sv @@ -120,7 +120,7 @@ module datamover_top ); // The "engine", i.e., the datapath of the HWPE, is as simple as it gets: - // a FIFO copying the data in stream into the data out one! + // a FIFO copying the data in stream into the data out one! ToDo(cdurrer): update comment datamover_engine #( .FIFO_DEPTH ( 4 ), .BANDWIDTH_ALIGNED ( BANDWIDTH_ALIGNED ), diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py index 2c255a9..ecc128c 100644 --- a/verif/python/generate_stimuli_test.py +++ b/verif/python/generate_stimuli_test.py @@ -172,9 +172,14 @@ def main(): # if ((MATRIX_SIZE_N * ELEM_WIDTH) < BANDWIDTH_ALIGNED): # raise ValueError("[GM] Matrix width (N) in bits must be at least as large as BANDWIDTH_ALIGNED.") # read_tot_length must not exceed 12-bit register capacity (4096) + # BANDWIDTH_ALIGNED must be a power of 2 + if ((BANDWIDTH_ALIGNED & (BANDWIDTH_ALIGNED - 1)) != 0) or (BANDWIDTH_ALIGNED < WORD_SIZE_BITS): + raise ValueError(f"[GM] BANDWIDTH_ALIGNED ({BANDWIDTH_ALIGNED}) must be a power of 2 and greater than the WORD_SIZE ({WORD_SIZE_BITS}).") + if ((args.read_tot_length >= 4096) & (args.datamover_mode != 0)): raise ValueError("[GM] read_tot_length (MxN / BW_ELEM) must be less than 4096 in transpose and CIM modes (12-bit register limit).") + if(args.datamover_mode == 1): # transpose mode # transp_mode must be valid (1=1elem, 2=2elem, 4=4elem) ToDo(cdurrer): update with datamover_mode etc if args.transp_mode not in [1, 2, 4]: From a587f1c7db786766b3632b2142001f00caa13f7c Mon Sep 17 00:00:00 2001 From: cdurrer Date: Thu, 20 Nov 2025 15:44:49 +0100 Subject: [PATCH 22/29] [mk,rtl] cleanup and deactivated strb for misaligned access (not yet working properly) --- config.mk | 4 ++-- rtl/datamover_engine.sv | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config.mk b/config.mk index e532895..2055a71 100644 --- a/config.mk +++ b/config.mk @@ -59,8 +59,8 @@ STIM_TRANSP_LEN ?= 0 STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) STIM_WRITE_D0_LENGTH ?= $(STIM_READ_D0_LENGTH) STIM_WRITE_D0_STRIDE ?= $(STIM_READ_D0_STRIDE) -STIM_WRITE_D1_LENGTH ?= $(STIM_READ_D1_LENGTH) -STIM_WRITE_D1_STRIDE ?= $(STIM_READ_D1_STRIDE) +STIM_WRITE_D1_LENGTH ?= 0 +STIM_WRITE_D1_STRIDE ?= 0 STIM_WRITE_D2_STRIDE ?= 0 STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) diff --git a/rtl/datamover_engine.sv b/rtl/datamover_engine.sv index 3c2ed99..f000e34 100644 --- a/rtl/datamover_engine.sv +++ b/rtl/datamover_engine.sv @@ -145,7 +145,7 @@ module datamover_engine .pop_o ( data_out ) ); // assign data_out_prefifo.strb = '1; // FIXME for leftovers ToDo(cdurrer): use for partial tiles - assign data_out_prefifo.strb = (tot_cnt_q >= 7-1) ? (1 << REMAINING_ELEMS) - 1 : '1; + assign data_out_prefifo.strb = '1; //(tot_cnt_q >= 7-1) ? (1 << REMAINING_ELEMS) - 1 : '1; assign data_out_prefifo.data = data_out_unrolled; assign data_out_prefifo.valid = data_out_valid; From f97bc0a7a4bc4c4e91dfd39b1987098d8a0308a3 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Thu, 20 Nov 2025 17:18:23 +0100 Subject: [PATCH 23/29] [mk,tb] Cleaned up test target structure and presets, added header file generation containing stimuli and golden outputs [gm] renamed golden model script [gm] Extended golden model to write all relevant configuration values to data.h [rtl] Added AW parameter propagation to solve assertion errors [tb,gm] Started adding mode 3: in-layout transposition (not yet complete) --- Bender.lock | 2 +- Bender.yml | 2 +- CONFIG_USAGE.md | 2 +- Makefile | 18 +- README.md | 3 +- config.mk | 67 ++- config_presets.mk | 4 +- rtl/datamover_streamer.sv | 18 +- verif/python/generate_stimuli.py | 591 +++++++++++++++----------- verif/python/generate_stimuli_test.py | 253 ----------- verif/python/validate_config.py | 44 +- verif/tb/tb_package.sv | 2 +- 12 files changed, 434 insertions(+), 572 deletions(-) delete mode 100644 verif/python/generate_stimuli_test.py diff --git a/Bender.lock b/Bender.lock index 224983c..b198637 100644 --- a/Bender.lock +++ b/Bender.lock @@ -49,7 +49,7 @@ packages: - redundancy_cells - register_interface hwpe-ctrl: - revision: d63ee3ec17af9b0cce0640fa88475bfeb0c5c93d + revision: 4977b6cf42b96f3fefd1973281d5c4b8259c50d5 version: null source: Git: https://github.com/pulp-platform/hwpe-ctrl.git diff --git a/Bender.yml b/Bender.yml index f471625..645c466 100644 --- a/Bender.yml +++ b/Bender.yml @@ -8,7 +8,7 @@ package: dependencies: hwpe-stream: { git: "https://github.com/pulp-platform/hwpe-stream.git", rev: 40ab0fe1433dc080a49aa1926ce09df9ab3f5fb5 } # branch: prasadar/multi-precision hci: { git: "https://github.com/pulp-platform/hci.git", rev: 9844a893c34612ac66697e8dcd60214296f2634c } # branch: prasadar/multi-precision - hwpe-ctrl: { git: "https://github.com/pulp-platform/hwpe-ctrl.git", rev: d63ee3ec17af9b0cce0640fa88475bfeb0c5c93d } # version: 2.0 } + hwpe-ctrl: { git: "https://github.com/pulp-platform/hwpe-ctrl.git", rev: 4977b6cf42b96f3fefd1973281d5c4b8259c50d5 } # version: 2.0 } sources: diff --git a/CONFIG_USAGE.md b/CONFIG_USAGE.md index ce55d4f..c2ff9c7 100644 --- a/CONFIG_USAGE.md +++ b/CONFIG_USAGE.md @@ -114,7 +114,7 @@ Parameters are resolved in this order (highest priority first): | `copy-medium` | Copy mode testing | 64x64 | 16KB | Copy | | `cim-small` | CIM mode testing | 32x128 | 32KB | CIM | | `cim-medium` | CIM mode testing | 64x256 | 64KB | CIM | -| `cim-large` | CIM mode testing | 128x512 | 256KB | CIM | +| `cim-large` | CIM mode testing | 128x256 | 256KB | CIM | | `custom` | User-defined | Variable | Variable | Variable | ## � Datamover Modes diff --git a/Makefile b/Makefile index bdbf7ab..299e3a5 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ help: @echo " copy-medium : 64x64 matrix (copy mode)" @echo " cim-small : 32x128 matrix (CIM mode)" @echo " cim-medium : 64x256 matrix (CIM mode)" - @echo " cim-large : 128x512 matrix (CIM mode)" + @echo " cim-large : 128x256 matrix (CIM mode)" @echo " custom : User-defined (config.mk default)" @echo "" @echo "Usage examples:" @@ -318,29 +318,17 @@ sim: stimuli sim-script validate-config clean-stimuli: rm -rf $(STIMULI_DIR) -stimuli: clean-stimuli - python -m verif.python.generate_stimuli_test \ +stimuli: clean-stimuli validate-config + python -m verif.python.generate_stimuli \ --mem_size $(STIM_MEM_SIZE) \ --read_base_addr $(STIM_READ_BASE_ADDR) \ - --read_d0_stride $(STIM_READ_D0_STRIDE) \ - --read_d0_length $(STIM_READ_D0_LENGTH) \ - --read_d1_stride $(STIM_READ_D1_STRIDE) \ - --read_d1_length $(STIM_READ_D1_LENGTH) \ - --read_d2_stride $(STIM_READ_D2_STRIDE) \ - --read_tot_length $(STIM_READ_TOT_LENGTH) \ --write_base_addr $(STIM_WRITE_BASE_ADDR) \ - --write_d0_stride $(STIM_WRITE_D0_STRIDE) \ - --write_d0_length $(STIM_WRITE_D0_LENGTH) \ - --write_d1_stride $(STIM_WRITE_D1_STRIDE) \ - --write_d1_length $(STIM_WRITE_D1_LENGTH) \ - --write_d2_stride $(STIM_WRITE_D2_STRIDE) \ --bandwidth_bits $(BANDWIDTH) \ --num_elem_word $(NUM_ELEM_WORD) \ --elem_width $(ELEM_WIDTH) \ --misaligned_accesses $(MISALIGNED_ACCESSES) \ --datamover_mode $(DATAMOVER_MODE) \ --transp_mode $(STIM_TRANSP_MODE) \ - --transp_len $(STIM_TRANSP_LEN) \ --cim_mode $(CIM_MODE) \ --cim_inner_dim $(CIM_INNER_DIM) \ --cim_outer_dim $(CIM_OUTER_DIM) \ diff --git a/README.md b/README.md index 3a47c33..bfd54ae 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,8 @@ The system includes predefined test configurations: - `copy-medium`: 64×64 matrix (copy mode testing) - `cim-small`: 32×128 matrix (CIM mode, 32 inner_dim, 128-bit bandwidth) - `cim-medium`: 64×256 matrix (CIM mode, 64 inner_dim, 256-bit bandwidth) -- `cim-large`: 128×512 matrix (CIM mode, 64 inner_dim, 512-bit bandwidth) +- `cim-large`: 128×256 matrix (CIM mode, 64 inner_dim, 512-bit bandwidth) +- `custom`: User-defined (config.mk default) For detailed configuration documentation, see `CONFIG_USAGE.md`. diff --git a/config.mk b/config.mk index 2055a71..27173a2 100644 --- a/config.mk +++ b/config.mk @@ -13,17 +13,17 @@ ####################### # Hardware configuration (can be overridden by presets or command line) -BANDWIDTH ?= 64 # in bits, multiple of WORD_WIDTH -WORD_WIDTH ?= 32 # in bits, multiple of ELEM_WIDTH -ELEM_WIDTH ?= 8 # in bits -MEMORY_SIZE ?= 512 # in words -MISALIGNED_ACCESSES ?= 1 +BANDWIDTH ?= 64 # in bits, multiple of WORD_WIDTH (512) +WORD_WIDTH ?= 32 # in bits, multiple of ELEM_WIDTH (64) +ELEM_WIDTH ?= 8 # in bits (8) +MEMORY_SIZE ?= 65536 # in words +MISALIGNED_ACCESSES ?= 0 -DATAMOVER_MODE ?= 0 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion +DATAMOVER_MODE ?= 0 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion, 3 = CIM data layout transpose TRANSP_MODE ?= 1 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> A-Layout, 1: row-major -> B-Layout -CIM_INNER_DIM ?= 32 # Inner dimension of the CIM accelerator (in elements): 64 for 64x8 CIM macro -CIM_OUTER_DIM ?= 32 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros +CIM_INNER_DIM ?= 64 # Inner dimension of the CIM accelerator (in elements): 64 for 64x8 CIM macro +CIM_OUTER_DIM ?= 64 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros # Input matrix dimensions (in elements) MATRIX_SIZE_M ?= 8 # Matrix height in elements @@ -46,11 +46,11 @@ $(info Copy mode enabled) STIM_READ_BASE_ADDR ?= 0 # Element-addressed STIM_READ_D0_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS)))) # [Nof accesses with bandwidth BW per D0-transfer ("row")] ToDo(cdurrer): not working for misaligned matrices STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -STIM_READ_D1_LENGTH ?= 0 # [Number of full D0-transfers ("rows")] -STIM_READ_D1_STRIDE ?= 0 # [Elements] -> manually compute "next row" stride -STIM_READ_D2_LENGTH ?= 0 # Not used for copy mode -STIM_READ_D2_STRIDE ?= 0 # Not used for copy mode -STIM_READ_TOT_LENGTH ?= $(MATRIX_SIZE_TOT) # [Total memory accesses] +STIM_READ_D1_LENGTH ?= 0 # Not used in copy mode +STIM_READ_D1_STRIDE ?= 0 # Not used in copy mode +STIM_READ_D2_LENGTH ?= 0 # Not used in copy mode +STIM_READ_D2_STRIDE ?= 0 # Not used in copy mode +STIM_READ_TOT_LENGTH ?= $(STIM_READ_D0_LENGTH) # [Total memory accesses] STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= 0 @@ -75,8 +75,8 @@ STIM_READ_D0_LENGTH ?= $(MATRIX_SIZE_M) # [Nof accesses with bandwid STIM_READ_D0_STRIDE ?= $(MATRIX_SIZE_N) # [Elements] STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # [Number of full D0-transfers ("rows")] STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -> manually compute "next row" stride -STIM_READ_D2_LENGTH ?= 0 # Not used for read -STIM_READ_D2_STRIDE ?= 0 # Not used for read +STIM_READ_D2_LENGTH ?= 0 # Not used in transpose mode +STIM_READ_D2_STRIDE ?= 0 # Not used in transpose mode STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] @@ -93,7 +93,7 @@ STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as else ifeq "$(strip $(DATAMOVER_MODE))" "2" # CIM data layout conversion mode $(info CIM data layout conversion mode $(CIM_MODE) enabled) -ifneq ($(filter 0 1,$(strip $(CIM_MODE))), $(strip $(CIM_MODE))) +ifneq ($(filter 0 1, $(strip $(CIM_MODE))), $(strip $(CIM_MODE))) $(error "Invalid CIM_MODE $(CIM_MODE): must be 0 or 1") endif STIM_READ_BASE_ADDR ?= 0 # Element-addressed @@ -101,7 +101,7 @@ STIM_READ_D0_LENGTH ?= $(shell echo $$(($(CIM_INNER_DIM) / $(BANDWIDTH_ELEMS)))) STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] STIM_READ_D1_LENGTH ?= $(MATRIX_SIZE_M) STIM_READ_D1_STRIDE ?= $(MATRIX_SIZE_N) -STIM_READ_D2_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(CIM_INNER_DIM)))) +STIM_READ_D2_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(CIM_INNER_DIM)))) # Redundant (handled by TOT_LEN) STIM_READ_D2_STRIDE ?= $(CIM_INNER_DIM) PARTIAL_MULT = $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(PARTIAL_MULT) * $(STIM_READ_D2_LENGTH)))) # [Total memory accesses] @@ -118,8 +118,39 @@ STIM_WRITE_D1_STRIDE ?= $(shell echo $$(($(STIM_WRITE_D0_LENGTH) * $(BANDWIDTH_E STIM_WRITE_D2_STRIDE ?= 0 STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read +else ifeq "$(strip $(DATAMOVER_MODE))" "3" # CIM data layout transpose mode +$(info CIM data layout transpose mode $(CIM_MODE) enabled) +ifneq ($(filter 0 1, $(strip $(CIM_MODE))), $(strip $(CIM_MODE))) + $(error "Invalid CIM_MODE $(CIM_MODE): must be 0 or 1") +endif +ifneq ($(filter 1 2 4,$(strip $(TRANSP_MODE))), $(strip $(TRANSP_MODE))) + $(error Invalid TRANSP_MODE $(TRANSP_MODE): must be 1, 2, or 4) +endif + +STIM_READ_BASE_ADDR ?= 0 # Element-addressed +STIM_READ_D0_LENGTH ?= $(MATRIX_SIZE_M) +STIM_READ_D0_STRIDE ?= $(CIM_INNER_DIM) +STIM_READ_D1_LENGTH ?= $(shell echo $$(($(CIM_INNER_DIM) / $(BANDWIDTH_ELEMS)))) +STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) +STIM_READ_D2_LENGTH ?= 0 # Not used (controlled by TOT_LEN) +STIM_READ_D2_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(CIM_INNER_DIM)))) +STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS)))) + +STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] +STIM_TRANSP_MODE ?= $(TRANSP_MODE) # transp_mode +STIM_TRANSP_LEN ?= 0 + +STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed +STIM_WRITE_D0_LENGTH ?= $(BANDWIDTH_ELEMS) +STIM_WRITE_D0_STRIDE ?= $(CIM_INNER_DIM) +STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(CIM_INNER_DIM) / $(BANDWIDTH_ELEMS)))) +STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) +STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(BANDWIDTH_ELEMS) * $(CIM_INNER_DIM)))) +STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read + + else -$(error "Invalid DATAMOVER_MODE $(DATAMOVER_MODE): must be 0 (copy), 1 (transpose), or 2 (CIM data layout conversion)") +$(error "Invalid DATAMOVER_MODE $(DATAMOVER_MODE): must be 0 (copy), 1 (transpose), 2 (CIM data layout conversion), or 3 (CIM data layout transpose)") endif # Debug: Print computed values (uncomment to see values during make) diff --git a/config_presets.mk b/config_presets.mk index d3a518b..da7b270 100644 --- a/config_presets.mk +++ b/config_presets.mk @@ -22,7 +22,7 @@ # - copy-medium : Medium 64x64 matrix for copy mode testing # - cim-small : CIM 32x128 matrix, CIM_INNER_DIM=32, 128-bit bandwidth # - cim-medium : CIM 64x256 matrix, CIM_INNER_DIM=64, 256-bit bandwidth -# - cim-large : CIM 128x512 matrix, CIM_INNER_DIM=64, 512-bit bandwidth +# - cim-large : CIM 128x256 matrix, CIM_INNER_DIM=64, 512-bit bandwidth # - custom : User-defined configuration (default) # Select configuration preset (can be overridden via command line) @@ -234,7 +234,7 @@ ifeq ($(CONFIG_PRESET),cim-large) CIM_OUTER_DIM = 64 MATRIX_SIZE_M = 128 MATRIX_SIZE_N = 256 - CONFIG_DESC = "CIM 128x512 matrix, CIM_INNER_DIM=64, 512-bit bandwidth" + CONFIG_DESC = "CIM 128x256 matrix, CIM_INNER_DIM=64, 512-bit bandwidth" endif ifeq ($(CONFIG_PRESET),custom) diff --git a/rtl/datamover_streamer.sv b/rtl/datamover_streamer.sv index e93842e..75a870c 100644 --- a/rtl/datamover_streamer.sv +++ b/rtl/datamover_streamer.sv @@ -14,6 +14,7 @@ /* * Authors: Francesco Conti * Sergio Mazzola + * Cyrill Durrer */ `include "hci_helpers.svh" @@ -49,6 +50,7 @@ module datamover_streamer ); localparam int unsigned BW = `HCI_SIZE_GET_BW(tcdm); + localparam int unsigned AW = `HCI_SIZE_GET_AW(tcdm); localparam int unsigned UW = `HCI_SIZE_GET_UW(tcdm); localparam int unsigned IW = `HCI_SIZE_GET_IW(tcdm); localparam int unsigned EW = `HCI_SIZE_GET_EW(tcdm); @@ -61,6 +63,7 @@ module datamover_streamer hci_core_intf #( .DW ( BANDWIDTH ), .BW ( BW ), + .AW ( AW ), .UW ( UW ), .IW ( IW ), .EW ( EW ), @@ -74,6 +77,7 @@ module datamover_streamer hci_core_intf #( .DW ( BANDWIDTH ), .BW ( BW ), + .AW ( AW ), .UW ( UW ), .IW ( IW ), .EW ( EW ), @@ -81,7 +85,7 @@ module datamover_streamer ) tcdm_prefifo ( .clk ( clk_i ) ); - + // "Virtual" TCDM interface, used to embody data after the TCDM FIFO // (if present) but before the load filter. Notice this is technically // an array of interfaces, with one single instance inside. This is @@ -89,10 +93,11 @@ module datamover_streamer hci_core_intf #( .DW ( BANDWIDTH ), .BW ( BW ), - .UW ( UW ), - .IW ( IW ), - .EW ( EW ), - .EHW ( EHW ) + .AW ( AW ), + .UW ( UW ), + .IW ( IW ), + .EW ( EW ), + .EHW ( EHW) ) tcdm_prefilter [0:0] ( .clk ( clk_i ) ); @@ -149,6 +154,7 @@ module datamover_streamer hci_core_load_store_mixer #( .DW ( BANDWIDTH ), .BW ( BW ), + .AW ( AW ), .UW ( UW ), .EW ( EW ) ) i_ld_st_mux_static ( @@ -194,7 +200,7 @@ module datamover_streamer endgenerate // The HCI core filter is meant to filter out r_valid strobes that the - // cluster may generate even when the TCDM access is a write. These + // cluster may generate even when the TCDM access is a write. These // pollute HCI TCDM FIFOs and mixers, and it is better to remove them // altogether. hci_core_r_valid_filter #( diff --git a/verif/python/generate_stimuli.py b/verif/python/generate_stimuli.py index 5e6659d..69b086b 100644 --- a/verif/python/generate_stimuli.py +++ b/verif/python/generate_stimuli.py @@ -1,180 +1,100 @@ import random import argparse import os - -def generate_random_hex_32bit(size): - """Generate random WORD_SIZE_BITS hex values.""" - def ceildiv(a, b): - return -(a // -b) - hex_length = ceildiv(WORD_SIZE_BITS, 4) # Each hex digit represents 4 bits - return [f"{random.randint(0, 2**WORD_SIZE_BITS - 1):0{hex_length}X}" for _ in range(size)] - -def generate_counting_hex_32bit(size): - """Generate counting series of 32-bit hex values (0, 1, 2, 3, ...).""" - return [f"{(i*4):08X}" for i in range(size)] - -def generate_counting_hex_8bit(size): +import math + +RANDOM_STIMULI = False # If False, counting stimuli are generated in a counting fashion +WORD_ALIGNED = False # If True, matrices are aligned to word boundaries by zero-padding + +# ToDo(cdurrer): small matrices (N < BW) not working + +def extract_elements_from_word(word, word_width, elem_width): + """Extract elements from a word based on the specified widths.""" + word_int = int(word, 16) # Convert hex string to integer + elements = [] + for i in range(word_width // elem_width): + # Extract each element using shift and mask + elem_val = (word_int >> (i * elem_width)) & ((1 << elem_width) - 1) + elements.append(elem_val) + return elements + +def convert_memory_to_vector(memory, elem_width, word_width): + """Convert memory (list of hex words) to a flat vector of elements.""" + vector = [] + for word in memory: + elements = extract_elements_from_word(word, word_width, elem_width) + vector.extend(elements) + return vector + +def data_header_format(data, elements_per_line=16): + lines = [] + for i in range(0, len(data), elements_per_line): + line_elements = data[i:i + elements_per_line] + formatted_elements = [f"0x{elem:02x}" for elem in line_elements] + # Add comma except for the last element + if i + elements_per_line < len(data): + line = " " + ", ".join(formatted_elements) + "," + else: + # Last line - no trailing comma + line = " " + ", ".join(formatted_elements) + lines.append(line) + return lines + +def generate_random_hex(size, word_width): + """Generate random word_width hex values.""" + hex_length = math.ceil(word_width / 4) # Each hex digit represents 4 bits + return [f"{random.randint(0, 2**word_width - 1):0{hex_length}X}" for _ in range(size)] + +def generate_counting_hex(size, elem_width, word_width): """ - Generate counting series of 32-bit hex values with 8-bit increments. - Each 32-bit word contains 4 consecutive 8-bit values in little-endian format. - Example: 03020100, 07060504, 0B0A0908, 0F0E0D0C, ... + Generate counting series of hex values with specified element and word widths. + Each word contains multiple elements in little-endian format. """ + elems_per_word = word_width // elem_width result = [] for i in range(size): - # Each word contains 4 consecutive bytes - base = i * 4 - byte0 = (base + 0) & 0xFF - byte1 = (base + 1) & 0xFF - byte2 = (base + 2) & 0xFF - byte3 = (base + 3) & 0xFF - - # Pack in little-endian format: byte3|byte2|byte1|byte0 - word = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0 - result.append(f"{word:08X}") - + word_val = 0 + for j in range(elems_per_word): + elem_val = (i * elems_per_word + j) & ((1 << elem_width) - 1) + word_val |= (elem_val << (j * elem_width)) + result.append(f"{word_val:0{word_width // 4}X}") # Format as hex string return result -def generate_addresses_2d(start, d0_stride, d0_length, d1_stride, d1_length, transactions, N): - """ - Generate addresses ensuring total transactions match, processing N words per transaction. - Strides are already adjusted with `>> elem_offset_bit` to align with word-based addressing. - """ - addresses = [] - addr = start - count = 0 - - print(f"Generating addresses (2D) starting from {hex(4*start)} [Byte Address] with N={N} words per transaction") - print(f"d0_stride: {d0_stride}, d0_length: {d0_length}, d1_stride: {d1_stride}, d1_length: {d1_length}") - - for d1 in range(d1_length): - addr_d1 = addr + d1 * d1_stride - print(f"Generating addresses for d1={d1} at {hex(4*addr_d1)} [Byte Address]") - for d0 in range(d0_length): - addr_d0 = addr_d1 + d0 * d0_stride # ToDo(cdurrer): shouldnt this be addr_d1* + d0 * d0_stride? - if addr_d0 + N <= MEMORY_SIZE: # Ensure full block fits - block = [addr_d0 + i for i in range(N)] - print(f" Appending address block: {', '.join(hex(4*a) for a in block)} [Byte Addresses]") - addresses.append([addr_d0 + i for i in range(N)]) # Read/Write N words - count += 1 - else: - print(f" Warning: Address block starting at {hex(4*addr_d0)} exceeds memory size. Skipping. [Byte Address]") - if count == transactions: # Stop when enough transactions are generated - print(f"Transaction limit reached! Generated total of {count} transactions.") - return addresses - - return addresses - -def generate_addresses_3d(start, d0_stride, d0_length, d1_stride, d1_length, d2_stride, d2_length, transactions, N): - """ - Generate addresses ensuring total transactions match, processing N words per transaction. - Strides are adjusted with `>> 2` to align with word-based addressing. - """ - addresses = [] - addr = start - count = 0 - - print(f"Generating addresses (3D) starting from {hex(start)} with N={N} words per transaction") - print(f"d0_stride: {d0_stride}, d0_length: {d0_length}, d1_stride: {d1_stride}, d1_length: {d1_length}, d2_stride: {d2_stride}, d2_length: {d2_length}") - - for d2 in range(d2_length): - addr_d2 = addr + d2 * d2_stride - # print(f"Generating addresses for d2={d2} at {hex(addr_d2)}") - print(f"Generating addresses for d2={d2} at {hex(4*addr_d2)} [Byte Address]") - for d1 in range(d1_length): - addr_d1 = addr_d2 + d1 * d1_stride - # print(f"Generating addresses for d1={d1} at {hex(addr_d1)}") - print(f" Generating addresses for d1={d1} at {hex(4*addr_d1)} [Byte Address]") - for d0 in range(d0_length): - addr_d0 = addr_d1 + d0 * d0_stride # ToDo(cdurrer): shouldnt this be addr_d1* + d0 * d0_stride? - if addr_d0 + N <= MEMORY_SIZE: # Ensure full block fits - block = [addr_d0 + i for i in range(N)] - # print(f"Appending address block: {', '.join(hex(a) for a in block)}") - print(f" Appending address block: {', '.join(hex(4*a) for a in block)} [Byte Addresses]") - addresses.append([addr_d0 + i for i in range(N)]) # Read/Write N words - count += 1 - else: - # print(f"Warning: Address block starting at {hex(addr_d0)} exceeds memory size. Skipping.") - print(f" Warning: Address block starting at {hex(4*addr_d0)} exceeds memory size. Skipping. [Byte Address]") - if count == transactions: # Stop when enough transactions are generated - print(f"Transaction limit reached! Generated total of {count} transactions.") - return addresses - - return addresses - -def generate_addresses_transpose_read(read_start, d0_length, d1_length, transp_len, transactions, transp_mode, N): - """ - Generate addresses for transposed access patterns. - Strides are already adjusted with `>> elem_offset_bit` to align with word-based addressing. - """ - read_addresses = [] - count = 0 - - print(f"Generating read addresses (Transpose Mode {transp_mode})") - print(f"d0_length: {d0_length}, d1_length: {d1_length}, N={N} words per transaction") - - for d1 in range(d1_length): - # addr_d1 = addr + d1 * d0_length*transp_mode # In transpose, d1 stride is d0_length - # print(f"Generating addresses for d1={d1} at {hex(4*addr_d1)} [Byte Address]") - for d0 in range(d0_length): - # addr_d0 = addr_d1 + d0 # In transpose, d0 stride is 1 - read_addr = read_start + d1*(d0_length*N) + d0 - if (read_addr + N <= MEMORY_SIZE): # Ensure full block fits - read_block = [read_addr + i for i in range(N)] - read_addresses.append(read_block) - - # Debug prints showing the actual appended address blocks - read_byte_addrs = [hex(4*addr) for addr in read_block] - print(f" Appending read address block: {', '.join(read_byte_addrs)} [Byte Addresses]") - - count += 1 - else: - print(f" Warning: Read/Write address block starting at {hex(4*read_addr)} exceeds memory size. Skipping. [Byte Address]") - if count == transactions: # Stop when enough transactions are generated - print(f"Transaction limit reached! Generated total of {count} transactions.") - return read_addresses - return read_addresses - -def generate_addresses_transpose_write(write_start, d0_length, d1_length, transp_len, transactions, transp_mode, N): - """ - Generate addresses for transposed access patterns. - Strides are already adjusted with `>> elem_offset_bit` to align with word-based addressing. - """ - write_addresses = [] - count = 0 - - print(f"Generating write addresses (Transpose Mode {transp_mode})") - print(f"d0_length: {d0_length}, d1_length: {d1_length}, N={N} words per transaction") - - for d1 in range(d1_length): - # addr_d1 = addr + d1 * d0_length*transp_mode # In transpose, d1 stride is d0_length - # print(f"Generating addresses for d1={d1} at {hex(4*addr_d1)} [Byte Address]") - for d0 in range(d0_length): - # addr_d0 = addr_d1 + d0 # In transpose, d0 stride is 1 - write_addr = write_start + d0*(d1_length*N) + (d1*N) - if (write_addr + N <= MEMORY_SIZE): # Ensure full block fits - write_block = [write_addr + i for i in range(N)] - write_addresses.append(write_block) - - # Debug prints showing the actual appended address blocks - write_byte_addrs = [hex(4*addr) for addr in write_block] - print(f" Appending write address block: {', '.join(write_byte_addrs)} [Byte Addresses]") - - count += 1 - else: - print(f" Warning: Read/Write address block starting at {hex(4*write_addr)} exceeds memory size. Skipping. [Byte Address]") - if count == transactions: # Stop when enough transactions are generated - print(f"Transaction limit reached! Generated total of {count} transactions.") - return write_addresses - return write_addresses - - -def update_memory(memory, write_addresses, extracted_data): - """Update memory with extracted data using generated write addresses, processing N words at a time.""" - for addr_block, data_block in zip(write_addresses, extracted_data): - for addr, value in zip(addr_block, data_block): - if addr < len(memory): - memory[addr] = value # Write N words at a time - print(f"Writing value {value} to address {hex(4*addr)}") +def pack_elements_to_word(elements, elem_width, word_width): + """Pack multiple elements into a single word.""" + elems_per_word = word_width // elem_width + word_val = 0 + for i, elem in enumerate(elements[:elems_per_word]): # Take only what fits in a word + word_val |= (elem << (i * elem_width)) + return f"{word_val:0{word_width // 4}X}" + +def matrix_to_hex_words(matrix, elem_width, word_width): + """Convert a matrix of elements to a list of hex words.""" + elems_per_word = word_width // elem_width + hex_words = [] + matrix_flat = sum(matrix, []) + for i in range(math.ceil(len(matrix_flat) / elems_per_word)): + hex_word = pack_elements_to_word(matrix_flat[i*elems_per_word:i*elems_per_word+elems_per_word], elem_width, word_width) + hex_words.append(hex_word) + return hex_words + +def matrix_to_hex_words_word_aligned(matrix, elem_width, word_width): + """Convert a matrix of elements to a list of hex words - aligned to word boundaries by zero-padding.""" + elems_per_word = word_width // elem_width + hex_words = [] + for row in matrix: + # Process each row, grouping elements into words + for i in range(0, len(row), elems_per_word): + elements_for_word = row[i:i + elems_per_word] + + # Pad with zeros if the row doesn't fill a complete word + while len(elements_for_word) < elems_per_word: + elements_for_word.append(0) + + # Pack elements into a word + hex_word = pack_elements_to_word(elements_for_word, elem_width, word_width) + hex_words.append(hex_word) + return hex_words def write_file(output_dir, filename, content): """Write list content to a file.""" @@ -183,112 +103,281 @@ def write_file(output_dir, filename, content): with open(filepath, "w") as file: file.write("\n".join(content) + "\n") +def write_data_header_file(output_dir, input_matrix, output_matrix, config_params): + """Write input and output matrices to a C header file with configuration parameters.""" + os.makedirs(output_dir, exist_ok=True) # Ensure directory exists + filepath = os.path.join(output_dir, "data.h") + size_m = len(input_matrix) + size_n = len(input_matrix[0]) if size_m > 0 else 0 + + input_flat = [elem for row in input_matrix for elem in row] + output_flat = [elem for row in output_matrix for elem in row] + + data_h_string = [ + "#pragma once", + "", + "#include ", + "", + "// Configuration Parameters", + f"#define BANDWIDTH {config_params['bandwidth']}", + f"#define WORD_WIDTH {config_params['word_width']}", + f"#define ELEM_WIDTH {config_params['elem_width']}", + f"#define MEMORY_SIZE {config_params['memory_size']}", + f"#define MISALIGNED_ACCESSES {config_params['misaligned_accesses']}", + f"#define DATAMOVER_MODE {config_params['datamover_mode']}", + f"#define TRANSP_MODE {config_params['transp_mode']}", + f"#define CIM_MODE {config_params['cim_mode']}", + f"#define CIM_INNER_DIM {config_params['cim_inner_dim']}", + f"#define CIM_OUTER_DIM {config_params['cim_outer_dim']}", + f"#define SIZE_M {config_params['matrix_size_m']}", + f"#define SIZE_N {config_params['matrix_size_n']}", + "", + "uint8_t golden_in [SIZE_M*SIZE_N] = {", + ] + data_h_string.extend(data_header_format(input_flat)) + data_h_string.extend([ + "};", + "", + "uint8_t golden_out [SIZE_M*SIZE_N] = {", + ]) + data_h_string.extend(data_header_format(output_flat)) + data_h_string.extend([ + "};", + "" + ]) + with open(filepath, "w") as file: + file.write("\n".join(data_h_string)) + return + +def transpose(matrix, size_n, size_m, transp_mode): + transposed = [[0 for _ in range(size_m * transp_mode)] for _ in range(size_n // transp_mode)] + for d1 in range(size_m): + for d0 in range(size_n // transp_mode): + for i in range(transp_mode): + # print(f"Transposing element [{d1}][{(d0*transp_mode)+i}] to [{d0}][{(d1*transp_mode)+i}]") + transposed[d0][(d1*transp_mode)+i] = matrix[d1][(d0*transp_mode)+i] + return transposed + +def cim_layout(matrix, size_n, size_m, cim_mode, cim_inner_dim, cim_outer_dim): + if cim_mode == 0: # row-major -> A-Layout + row_tile_size = cim_inner_dim + initial_size_m = size_m + initial_size_n = size_n + elif cim_mode == 1: # row-major -> B-Layout + row_tile_size = cim_outer_dim + initial_size_m = size_m + initial_size_n = size_n + elif cim_mode == 2: # A-Layout -> row-major + row_tile_size = cim_inner_dim + initial_size_m = size_n // row_tile_size + initial_size_n = size_m * row_tile_size + elif cim_mode == 3: # B-Layout -> row-major + row_tile_size = cim_outer_dim + initial_size_m = size_n // row_tile_size + initial_size_n = size_m * row_tile_size + else: + raise ValueError("[GM] cim_mode must be 0 (A-Layout), 1 (B-Layout), 2 (A-Layout -> row-major), or 3 (B-Layout -> row-major).") + + print(f"initial_size_m: {initial_size_m}, initial_size_n: {initial_size_n}, row_tile_size: {row_tile_size}") + cim_matrix = [[0 for _ in range(initial_size_m * row_tile_size)] for _ in range(initial_size_n // row_tile_size)] + for d2 in range(initial_size_n // row_tile_size): + for d1 in range(initial_size_m): + cim_matrix[d2][(d1*row_tile_size):(d1*row_tile_size+row_tile_size)] = matrix[d1][d2*(row_tile_size):(d2*row_tile_size+row_tile_size)] + + # Reshape back to original dimensions for output + + print(f"CIM Matrix Size: {len(cim_matrix)} x {len(cim_matrix[0])}") + + return cim_matrix + def main(): - # Parse command-line arguments + # Parse command-line arguments parser = argparse.ArgumentParser(description="Memory Read/Write Simulation with Word-Aligned Strides") - parser.add_argument("--mem_size", type=int, default=0x30, help="Memory size in entries") + parser.add_argument("--mem_size", type=int, default=0x30, help="Memory size in words") parser.add_argument("--read_base_addr", type=int, default=0x00, help="Base address for read operations") parser.add_argument("--write_base_addr", type=int, default=0x20, help="Base address for write operations") - parser.add_argument("--read_d0_stride", type=int, default=4, help="Stride for d0 read (in bytes)") - parser.add_argument("--read_d1_stride", type=int, default=16, help="Stride for d1 read (in bytes)") - parser.add_argument("--read_d0_length", type=int, default=4, help="Length for d0 read") - parser.add_argument("--read_d1_length", type=int, default=4, help="Length for d1 read") - parser.add_argument("--write_d0_stride", type=int, default=4, help="Stride for d0 write (in bytes)") - parser.add_argument("--write_d1_stride", type=int, default=16, help="Stride for d1 write (in bytes)") - parser.add_argument("--write_d0_length", type=int, default=4, help="Length for d0 write") - parser.add_argument("--write_d1_length", type=int, default=4, help="Length for d1 write") parser.add_argument("--bandwidth_bits", type=int, default=4, help="Number of bits per transaction") parser.add_argument("--num_elem_word", type=int, default=4, help="Number of elements in a memory bank word") parser.add_argument("--elem_width", type=int, default=8, help="Width of each element (in bits)") + parser.add_argument("--misaligned_accesses", type=int, default=0, help="Enable misaligned accesses (0=disabled, 1=enabled)") + parser.add_argument("--datamover_mode", type=int, default=0, help="Datamover mode (0=normal, 1=CIM)") parser.add_argument("--transp_mode", type=int, default=0, help="Transposition mode (3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem)") - parser.add_argument("--transp_len", type=int, default=0, help="Transposition length") + parser.add_argument("--cim_mode", type=int, default=0, help="CIM mode (0=normal, 1=CIM)") + parser.add_argument("--cim_inner_dim", type=int, default=4, help="CIM inner dimension") + parser.add_argument("--cim_outer_dim", type=int, default=4, help="CIM outer dimension") + parser.add_argument("--matrix_size_m", type=int, default=64, help="Matrix height in elements") + parser.add_argument("--matrix_size_n", type=int, default=64, help="Matrix width in elements") parser.add_argument("--output_dir", type=str, default="output", help="Directory for storing output files") args = parser.parse_args() - global MEMORY_SIZE - global WORD_SIZE_BITS + BANDWIDTH_ALIGNED = args.bandwidth_bits - (args.misaligned_accesses * (args.elem_width * args.num_elem_word)) MEMORY_SIZE = args.mem_size # Set global memory size + BANDWIDTH_ELEMS = BANDWIDTH_ALIGNED // args.elem_width WORD_SIZE_BITS = args.num_elem_word * args.elem_width # Set global word size in bits + ELEM_WIDTH = args.elem_width + WORD_WIDTH = args.num_elem_word * args.elem_width + READ_BASE_ADDR = args.read_base_addr + WRITE_BASE_ADDR = args.write_base_addr + TRANSP_MODE = args.transp_mode + MATRIX_SIZE_N = args.matrix_size_n + MATRIX_SIZE_M = args.matrix_size_m + TOT_LENGTH = (args.matrix_size_m * args.matrix_size_n) // BANDWIDTH_ELEMS + + OUTPUT_DIR = args.output_dir + + if MEMORY_SIZE < ((MATRIX_SIZE_N * MATRIX_SIZE_M * ELEM_WIDTH // WORD_WIDTH) * 2): + raise ValueError(f"MEMORY_SIZE ({MEMORY_SIZE}) is too small for the given matrix size " + f"({MATRIX_SIZE_M}x{MATRIX_SIZE_N}) and element width ({ELEM_WIDTH})") # num_elem_word must be power of two and greater than zero if args.num_elem_word & (args.num_elem_word - 1) != 0 or args.num_elem_word <= 0: - raise ValueError("num_elem_word must be a power of two and greater than zero.") + raise ValueError("[GM] num_elem_word must be a power of two and greater than zero.") # bandwidth width must be a multiple of word size - if args.bandwidth_bits % WORD_SIZE_BITS != 0: - raise ValueError("bandwidth_bits must be a multiple of the word size (num_elem_word * elem_width).") - # no transposition currently supported - # if args.transp_mode != 0: - # raise NotImplementedError("Transposition modes other than 'none' are not currently supported.") - - bandwidth_N = args.bandwidth_bits // WORD_SIZE_BITS - - print(f"Memory Size: {MEMORY_SIZE} entries") - - # Step 1: Generate initial memory - # memory = generate_random_hex_32bit(MEMORY_SIZE) # For testing - # memory = generate_counting_hex_32bit(MEMORY_SIZE) # For debugging - memory = generate_counting_hex_8bit(MEMORY_SIZE) # For debugging with 8-bit elements - - # Convert addresses from element-addressing (e.g., byte-addressing) to word-addressing - elem_offset_bit = (args.num_elem_word).bit_length() - 1 - - args.write_base_addr = args.write_base_addr >> elem_offset_bit - args.read_base_addr = args.read_base_addr >> elem_offset_bit - args.write_d0_stride = args.write_d0_stride >> elem_offset_bit - args.write_d1_stride = args.write_d1_stride >> elem_offset_bit - args.read_d0_stride = args.read_d0_stride >> elem_offset_bit - args.read_d1_stride = args.read_d1_stride >> elem_offset_bit - - if (args.transp_mode == 0): + if BANDWIDTH_ALIGNED % WORD_SIZE_BITS != 0: + raise ValueError("[GM] BANDWIDTH_ALIGNED must be a multiple of the word size (num_elem_word * elem_width).") + # # bandwidth width must be a multiple of word size + # if ((MATRIX_SIZE_N * ELEM_WIDTH) < BANDWIDTH_ALIGNED): + # raise ValueError("[GM] Matrix width (N) in bits must be at least as large as BANDWIDTH_ALIGNED.") + # read_tot_length must not exceed 12-bit register capacity (4096) - # Step 2: Generate read addresses (Word-aligned) - read_transactions = args.read_d0_length * args.read_d1_length - read_addresses = generate_addresses_2d( - args.read_base_addr, args.read_d0_stride, args.read_d0_length, - args.read_d1_stride, args.read_d1_length, read_transactions, bandwidth_N - ) + # # BANDWIDTH_ALIGNED must be a power of 2 + # if ((BANDWIDTH_ALIGNED & (BANDWIDTH_ALIGNED - 1)) != 0) or (BANDWIDTH_ALIGNED < WORD_SIZE_BITS): + # raise ValueError(f"[GM] BANDWIDTH_ALIGNED ({BANDWIDTH_ALIGNED}) must be a power of 2 and greater than the WORD_SIZE ({WORD_SIZE_BITS}).") - # Step 3: Extract memory values based on read addresses - extracted_data = [[memory[addr] for addr in block] for block in read_addresses if all(addr < MEMORY_SIZE for addr in block)] + if ((TOT_LENGTH >= 4096) & (args.datamover_mode != 0)): + raise ValueError("[GM] TOT_LENGTH (MxN / BW_ELEM) must be less than 4096 in transpose and CIM modes (12-bit register limit).") - # Step 4: Save initial memory - write_file(args.output_dir, "initial_memory.txt", memory) - # Step 5: Save debug info (addresses read and values extracted) - debug_info = [ - f"Read Block: {', '.join(hex(addr) for addr in block)} -> Data: {', '.join(memory[addr] for addr in block)}" - for block in read_addresses if all(addr < MEMORY_SIZE for addr in block) - ] - write_file(args.output_dir, "debug_values.txt", debug_info) + if(args.datamover_mode == 1): # transpose mode + # transp_mode must be valid (1=1elem, 2=2elem, 4=4elem) + if args.transp_mode not in [1, 2, 4]: + raise ValueError("[GM] transp_mode must be 1 (1 elem), 2 (2 elem), or 4 (4 elem).") + if (MATRIX_SIZE_N % args.transp_mode) != 0: + raise ValueError(f"[GM] Matrix width N ({MATRIX_SIZE_N}) must be a multiple of transp_mode ({args.transp_mode}).") - # Step 6: Generate write addresses (Word-aligned) - write_transactions = read_transactions - write_addresses = generate_addresses_2d(args.write_base_addr, args.write_d0_stride, args.write_d0_length, args.write_d1_stride, args.write_d1_length, write_transactions, bandwidth_N) - - else: # Transpose mode - print(f"Transposition mode {args.transp_mode} selected.") - if (args.transp_len == 0): - args.transp_len = args.bandwidth_bits // args.elem_width - - # Step 2: Generate read addresses (Word-aligned) - read_transactions = args.read_d0_length * args.read_d1_length - read_addresses = generate_addresses_transpose_read(args.read_base_addr, args.read_d0_length, args.read_d1_length, args.transp_len, read_transactions, args.transp_mode, bandwidth_N) - write_addresses = generate_addresses_transpose_write(args.write_base_addr, args.write_d0_length, args.write_d1_length, args.transp_len, read_transactions, args.transp_mode, bandwidth_N) - - # Step 3: Extract memory values based on read addresses - extracted_data = [[memory[addr] for addr in block] for block in read_addresses if all(addr < MEMORY_SIZE for addr in block)] - - # Step 4: Save initial memory - write_file(args.output_dir, "initial_memory.txt", memory) - - # Step 7: Update memory with extracted data at write addresses - update_memory(memory, write_addresses, extracted_data) - - # Step 8: Save updated memory - write_file(args.output_dir, "updated_memory.txt", memory) - - print(f"Files generated in '{args.output_dir}': initial_memory.txt, debug_values.txt, updated_memory.txt") + print(f"Memory Size: {MEMORY_SIZE} entries") + print(f"Word Size: {WORD_SIZE_BITS} bits") + + # memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) + if RANDOM_STIMULI: + memory = generate_random_hex(MEMORY_SIZE, WORD_SIZE_BITS) # for testing + else: + memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) # for debugging + + write_file(OUTPUT_DIR, "initial_memory.txt", memory) + + # Convert memory to flat vector + memory_flat = convert_memory_to_vector(memory, ELEM_WIDTH, WORD_WIDTH) + + # Extract matrix (read dimensions) from memory + input_matrix = [[0 for _ in range(MATRIX_SIZE_N)] for _ in range(MATRIX_SIZE_M)] + for d1 in range(MATRIX_SIZE_M): + row = [] + for d0 in range(MATRIX_SIZE_N): + input_matrix[d1][d0] = memory_flat[(READ_BASE_ADDR + d1 * MATRIX_SIZE_N + d0)] + + # Print input matrix + print("Input Matrix:") + # # for i, row in enumerate(input_matrix): + # # print(f"Row {i}: {row}") + # # print("\n") + for i, row in enumerate(input_matrix): + print(f"Row {i}: {[format(elem, 'X') for elem in row]}") + + if args.datamover_mode == 0: # Copy mode + output_matrix = input_matrix + elif args.datamover_mode == 1: # Transpose mode + output_matrix = transpose(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, TRANSP_MODE) + elif args.datamover_mode == 2: # CIM mode + output_matrix = cim_layout(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + # elif args.datamover_mode == 2: # TEST CIM mode (TWICE: should be identical to input) + # intermediate_matrix = cim_layout(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + # intermediate_size_m = len(intermediate_matrix) + # intermediate_size_n = len(intermediate_matrix[0]) + # # Print intermediate matrix + # print("\nIntermediate Matrix:") + # for i, row in enumerate(intermediate_matrix): + # print(f"Row {i}: {[format(elem, 'X') for elem in row]}") + # print("\n") + # output_matrix = cim_layout(intermediate_matrix, intermediate_size_n, intermediate_size_m, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + elif args.datamover_mode == 3: # CIM layout transpose mode (INPUT SIZES EXPECTED IN ORIGINAL (ROW-MAJOR) LAYOUT FORM!) + print("TRANSP_MODE = ", TRANSP_MODE) + converted_size_m = MATRIX_SIZE_N // args.cim_inner_dim + converted_size_n = MATRIX_SIZE_M * args.cim_inner_dim + # Reshape input_matrix to converted_size_m x converted_size_n + input_flat = [elem for row in input_matrix for elem in row] + reshaped_matrix = [ + [input_flat[i * converted_size_n + j] for j in range(converted_size_n)] + for i in range(converted_size_m)] + print("\nReshaped Matrix:") + for i, row in enumerate(reshaped_matrix): + print(f"Row {i}: {[format(elem, 'X') for elem in row]}") + print("\n") + + intermediate1_matrix = cim_layout(reshaped_matrix, converted_size_n, converted_size_m, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + intermediate1_size_m = len(intermediate1_matrix) + intermediate1_size_n = len(intermediate1_matrix[0]) + + print("\nIntermediate1 Matrix (CIM layout -> row-major):") + for i, row in enumerate(intermediate1_matrix): + print(f"Row {i}: {[format(elem, 'X') for elem in row]}") + print("\n") + intermediate2_matrix = transpose(intermediate1_matrix, intermediate1_size_n, intermediate1_size_m, TRANSP_MODE) + intermediate2_size_m = len(intermediate2_matrix) + intermediate2_size_n = len(intermediate2_matrix[0]) + print("\nIntermediate2 Matrix (row major transpose):") + for i, row in enumerate(intermediate2_matrix): + print(f"Row {i}: {[format(elem, 'X') for elem in row]}") + print("\n") + output_matrix = cim_layout(intermediate2_matrix, intermediate2_size_n, intermediate2_size_m, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + else: + raise ValueError("[GM] datamover_mode must be 0 (copy), 1 (transpose), or 2 (CIM).") + + # Print output matrix + print("\nOutput Matrix:") + for i, row in enumerate(output_matrix): + print(f"Row {i}: {[format(elem, 'X') for elem in row]}") + print("\n") + + # Compare input and output matrix: equality check + if (output_matrix == input_matrix): + print("Output matrix matches input matrix.") + + # # Convert output matrix back to words + if (WORD_ALIGNED): + output_hex_words = matrix_to_hex_words_word_aligned(output_matrix, ELEM_WIDTH, WORD_WIDTH) + else: + output_hex_words = matrix_to_hex_words(output_matrix, ELEM_WIDTH, WORD_WIDTH) + + # print(f"\nOutput Matrix as Hex Words:") + # for i, word in enumerate(output_hex_words): + # print(f"Word {i}: {word}") + + # Write back output matrix to memory at WRITE_BASE_ADDR + for i, word in enumerate(output_hex_words): + memory[(WRITE_BASE_ADDR // (WORD_WIDTH // 8) + i)] = word + # print(f"Writing word {word} to memory address [Byte-address] {WRITE_BASE_ADDR + i * (WORD_WIDTH // 8)}") + # print(f"Writing word {word} to memory address [Word-address] {WRITE_BASE_ADDR + i}") + + write_file(OUTPUT_DIR, "updated_memory.txt", memory) + + # Write data header file for C testing + config_params = { + 'bandwidth': args.bandwidth_bits, + 'word_width': WORD_WIDTH, + 'elem_width': args.elem_width, + 'memory_size': args.mem_size, + 'misaligned_accesses': args.misaligned_accesses, + 'datamover_mode': args.datamover_mode, + 'transp_mode': args.transp_mode, + 'cim_mode': args.cim_mode, + 'cim_inner_dim': args.cim_inner_dim, + 'cim_outer_dim': args.cim_outer_dim, + 'matrix_size_m': args.matrix_size_m, + 'matrix_size_n': args.matrix_size_n + } + write_data_header_file(OUTPUT_DIR, input_matrix, output_matrix, config_params) if __name__ == "__main__": main() diff --git a/verif/python/generate_stimuli_test.py b/verif/python/generate_stimuli_test.py deleted file mode 100644 index ecc128c..0000000 --- a/verif/python/generate_stimuli_test.py +++ /dev/null @@ -1,253 +0,0 @@ -import random -import argparse -import os -import math - -RANDOM_STIMULI = False # If False, counting stimuli are generated in a counting fashion -WORD_ALIGNED = False # If True, matrices are aligned to word boundaries by zero-padding - -# ToDo(cdurrer): small matrices (N < BW) not working - -def extract_elements_from_word(word, word_width, elem_width): - """Extract elements from a word based on the specified widths.""" - word_int = int(word, 16) # Convert hex string to integer - elements = [] - for i in range(word_width // elem_width): - # Extract each element using shift and mask - elem_val = (word_int >> (i * elem_width)) & ((1 << elem_width) - 1) - elements.append(elem_val) - return elements - -def convert_memory_to_vector(memory, elem_width, word_width): - """Convert memory (list of hex words) to a flat vector of elements.""" - vector = [] - for word in memory: - elements = extract_elements_from_word(word, word_width, elem_width) - vector.extend(elements) - return vector - -def generate_random_hex(size, word_width): - """Generate random word_width hex values.""" - hex_length = math.ceil(word_width / 4) # Each hex digit represents 4 bits - return [f"{random.randint(0, 2**word_width - 1):0{hex_length}X}" for _ in range(size)] - -def generate_counting_hex(size, elem_width, word_width): - """ - Generate counting series of hex values with specified element and word widths. - Each word contains multiple elements in little-endian format. - """ - elems_per_word = word_width // elem_width - result = [] - for i in range(size): - word_val = 0 - for j in range(elems_per_word): - elem_val = (i * elems_per_word + j) & ((1 << elem_width) - 1) - word_val |= (elem_val << (j * elem_width)) - result.append(f"{word_val:0{word_width // 4}X}") # Format as hex string - return result - -def pack_elements_to_word(elements, elem_width, word_width): - """Pack multiple elements into a single word.""" - elems_per_word = word_width // elem_width - word_val = 0 - for i, elem in enumerate(elements[:elems_per_word]): # Take only what fits in a word - word_val |= (elem << (i * elem_width)) - return f"{word_val:0{word_width // 4}X}" - -def matrix_to_hex_words(matrix, elem_width, word_width): - """Convert a matrix of elements to a list of hex words.""" - elems_per_word = word_width // elem_width - hex_words = [] - matrix_flat = sum(matrix, []) - for i in range(math.ceil(len(matrix_flat) / elems_per_word)): - hex_word = pack_elements_to_word(matrix_flat[i*elems_per_word:i*elems_per_word+elems_per_word], elem_width, word_width) - hex_words.append(hex_word) - return hex_words - -def matrix_to_hex_words_word_aligned(matrix, elem_width, word_width): - """Convert a matrix of elements to a list of hex words - aligned to word boundaries by zero-padding.""" - elems_per_word = word_width // elem_width - hex_words = [] - for row in matrix: - # Process each row, grouping elements into words - for i in range(0, len(row), elems_per_word): - elements_for_word = row[i:i + elems_per_word] - - # Pad with zeros if the row doesn't fill a complete word - while len(elements_for_word) < elems_per_word: - elements_for_word.append(0) - - # Pack elements into a word - hex_word = pack_elements_to_word(elements_for_word, elem_width, word_width) - hex_words.append(hex_word) - return hex_words - -def write_file(output_dir, filename, content): - """Write list content to a file.""" - os.makedirs(output_dir, exist_ok=True) # Ensure directory exists - filepath = os.path.join(output_dir, filename) - with open(filepath, "w") as file: - file.write("\n".join(content) + "\n") - -def transpose(matrix, size_n, size_m, transp_mode): - transposed = [[0 for _ in range(size_m * transp_mode)] for _ in range(size_n // transp_mode)] - for d1 in range(size_m): - for d0 in range(size_n // transp_mode): - for i in range(transp_mode): - # print(f"Transposing element [{d1}][{(d0*transp_mode)+i}] to [{d0}][{(d1*transp_mode)+i}]") - transposed[d0][(d1*transp_mode)+i] = matrix[d1][(d0*transp_mode)+i] - return transposed - -def cim_layout(matrix, size_n, size_m, cim_mode, cim_inner_dim, cim_outer_dim): - # ToDo(cdurrer): implement CIM mode - cim_matrix = [[0 for _ in range(size_m * cim_inner_dim)] for _ in range(size_n // cim_inner_dim)] - for d2 in range(size_n // cim_inner_dim): - for d1 in range(size_m): - cim_matrix[d2][(d1*cim_inner_dim):(d1*cim_inner_dim+cim_inner_dim)] = matrix[d1][d2*(cim_inner_dim):(d2*cim_inner_dim+cim_inner_dim)] - - return cim_matrix - -def main(): - # Parse command-line arguments - parser = argparse.ArgumentParser(description="Memory Read/Write Simulation with Word-Aligned Strides") - parser.add_argument("--mem_size", type=int, default=0x30, help="Memory size in words") - parser.add_argument("--read_base_addr", type=int, default=0x00, help="Base address for read operations") - parser.add_argument("--write_base_addr", type=int, default=0x20, help="Base address for write operations") - parser.add_argument("--read_d0_stride", type=int, default=4, help="Stride for d0 read (in bytes)") - parser.add_argument("--read_d1_stride", type=int, default=16, help="Stride for d1 read (in bytes)") - parser.add_argument("--read_d2_stride", type=int, default=64, help="Stride for d2 read (in bytes)") - parser.add_argument("--read_d0_length", type=int, default=4, help="Length for d0 read") - parser.add_argument("--read_d1_length", type=int, default=4, help="Length for d1 read") - parser.add_argument("--read_tot_length", type=int, default=16, help="Total read length") - parser.add_argument("--write_d0_stride", type=int, default=4, help="Stride for d0 write (in bytes)") - parser.add_argument("--write_d1_stride", type=int, default=16, help="Stride for d1 write (in bytes)") - parser.add_argument("--write_d2_stride", type=int, default=64, help="Stride for d2 write (in bytes)") - parser.add_argument("--write_d0_length", type=int, default=4, help="Length for d0 write") - parser.add_argument("--write_d1_length", type=int, default=4, help="Length for d1 write") - parser.add_argument("--bandwidth_bits", type=int, default=4, help="Number of bits per transaction") - parser.add_argument("--num_elem_word", type=int, default=4, help="Number of elements in a memory bank word") - parser.add_argument("--elem_width", type=int, default=8, help="Width of each element (in bits)") - parser.add_argument("--misaligned_accesses", type=int, default=0, help="Enable misaligned accesses (0=disabled, 1=enabled)") - parser.add_argument("--datamover_mode", type=int, default=0, help="Datamover mode (0=normal, 1=CIM)") - parser.add_argument("--transp_mode", type=int, default=0, help="Transposition mode (3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem)") - parser.add_argument("--transp_len", type=int, default=0, help="Transposition length") - parser.add_argument("--cim_mode", type=int, default=0, help="CIM mode (0=normal, 1=CIM)") - parser.add_argument("--cim_inner_dim", type=int, default=4, help="CIM inner dimension") - parser.add_argument("--cim_outer_dim", type=int, default=4, help="CIM outer dimension") - parser.add_argument("--matrix_size_m", type=int, default=64, help="Matrix height in elements") - parser.add_argument("--matrix_size_n", type=int, default=64, help="Matrix width in elements") - parser.add_argument("--output_dir", type=str, default="output", help="Directory for storing output files") - - args = parser.parse_args() - - BANDWIDTH_ALIGNED = args.bandwidth_bits - (args.misaligned_accesses * (args.elem_width * args.num_elem_word)) - MEMORY_SIZE = args.mem_size # Set global memory size - BANDWIDTH_ELEMS = BANDWIDTH_ALIGNED // args.elem_width - BANDWIDTH_WORDS = BANDWIDTH_ELEMS // args.num_elem_word - WORD_SIZE_BITS = args.num_elem_word * args.elem_width # Set global word size in bits - - ELEM_WIDTH = args.elem_width - WORD_WIDTH = args.num_elem_word * args.elem_width - NUM_ELEM_WORD = args.num_elem_word - READ_BASE_ADDR = args.read_base_addr - READ_D0_LENGTH = args.read_d0_length - READ_D1_LENGTH = args.read_d1_length - WRITE_BASE_ADDR = args.write_base_addr - TRANSP_MODE = args.transp_mode - MATRIX_SIZE_N = args.matrix_size_n - MATRIX_SIZE_M = args.matrix_size_m - - OUTPUT_DIR = args.output_dir - - if MEMORY_SIZE < ((MATRIX_SIZE_N * MATRIX_SIZE_M * ELEM_WIDTH // WORD_WIDTH) * 2): - raise ValueError(f"MEMORY_SIZE ({MEMORY_SIZE}) is too small for the given matrix size " - f"({MATRIX_SIZE_M}x{MATRIX_SIZE_N}) and element width ({ELEM_WIDTH})") - # num_elem_word must be power of two and greater than zero - if args.num_elem_word & (args.num_elem_word - 1) != 0 or args.num_elem_word <= 0: - raise ValueError("[GM] num_elem_word must be a power of two and greater than zero.") - # bandwidth width must be a multiple of word size - if BANDWIDTH_ALIGNED % WORD_SIZE_BITS != 0: - raise ValueError("[GM] BANDWIDTH_ALIGNED must be a multiple of the word size (num_elem_word * elem_width).") - # # bandwidth width must be a multiple of word size - # if ((MATRIX_SIZE_N * ELEM_WIDTH) < BANDWIDTH_ALIGNED): - # raise ValueError("[GM] Matrix width (N) in bits must be at least as large as BANDWIDTH_ALIGNED.") - # read_tot_length must not exceed 12-bit register capacity (4096) - # BANDWIDTH_ALIGNED must be a power of 2 - if ((BANDWIDTH_ALIGNED & (BANDWIDTH_ALIGNED - 1)) != 0) or (BANDWIDTH_ALIGNED < WORD_SIZE_BITS): - raise ValueError(f"[GM] BANDWIDTH_ALIGNED ({BANDWIDTH_ALIGNED}) must be a power of 2 and greater than the WORD_SIZE ({WORD_SIZE_BITS}).") - - if ((args.read_tot_length >= 4096) & (args.datamover_mode != 0)): - raise ValueError("[GM] read_tot_length (MxN / BW_ELEM) must be less than 4096 in transpose and CIM modes (12-bit register limit).") - - - if(args.datamover_mode == 1): # transpose mode - # transp_mode must be valid (1=1elem, 2=2elem, 4=4elem) ToDo(cdurrer): update with datamover_mode etc - if args.transp_mode not in [1, 2, 4]: - raise ValueError("[GM] transp_mode must be 1 (1 elem), 2 (2 elem), or 4 (4 elem).") - if (MATRIX_SIZE_N % args.transp_mode) != 0: - raise ValueError(f"[GM] Matrix width N ({MATRIX_SIZE_N}) must be a multiple of transp_mode ({args.transp_mode}).") - - print(f"Memory Size: {MEMORY_SIZE} entries") - print(f"Word Size: {WORD_SIZE_BITS} bits") - - # memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) - if RANDOM_STIMULI: - memory = generate_random_hex(MEMORY_SIZE, WORD_SIZE_BITS) # for testing - else: - memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) # for debugging - - write_file(OUTPUT_DIR, "initial_memory.txt", memory) - - # Convert memory to flat vector - memory_flat = convert_memory_to_vector(memory, ELEM_WIDTH, WORD_WIDTH) - - # Extract matrix (read dimensions) from memory - input_matrix = [[0 for _ in range(MATRIX_SIZE_N)] for _ in range(MATRIX_SIZE_M)] - for d1 in range(MATRIX_SIZE_M): - row = [] - for d0 in range(MATRIX_SIZE_N): - input_matrix[d1][d0] = memory_flat[(READ_BASE_ADDR + d1 * MATRIX_SIZE_N + d0)] - - # Print input matrix - print("Input Matrix:") - # # for i, row in enumerate(input_matrix): - # # print(f"Row {i}: {row}") - # # print("\n") - for i, row in enumerate(input_matrix): - print(f"Row {i}: {[format(elem, 'X') for elem in row]}") - - if args.datamover_mode == 0: # Copy mode - output_matrix = input_matrix - elif args.datamover_mode == 1: # Transpose mode - output_matrix = transpose(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, TRANSP_MODE) - elif args.datamover_mode == 2: # CIM mode - output_matrix = cim_layout(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) - else: - raise ValueError("[GM] datamover_mode must be 0 (copy), 1 (transpose), or 2 (CIM).") - - # Print output matrix - print("\nOutput Matrix:") - for i, row in enumerate(output_matrix): - print(f"Row {i}: {[format(elem, 'X') for elem in row]}") - print("\n") - - # # Convert output matrix back to words - if (WORD_ALIGNED): - output_hex_words = matrix_to_hex_words_word_aligned(output_matrix, ELEM_WIDTH, WORD_WIDTH) - else: - output_hex_words = matrix_to_hex_words(output_matrix, ELEM_WIDTH, WORD_WIDTH) - - print(f"\nOutput Matrix as Hex Words:") - for i, word in enumerate(output_hex_words): - print(f"Word {i}: {word}") - - # Write back output matrix to memory at WRITE_BASE_ADDR - for i, word in enumerate(output_hex_words): - memory[(WRITE_BASE_ADDR // (WORD_WIDTH // 8) + i)] = word - # print(f"Writing word {word} to memory address [Byte-address] {WRITE_BASE_ADDR + i * (WORD_WIDTH // 8)}") - # print(f"Writing word {word} to memory address [Word-address] {WRITE_BASE_ADDR + i}") - - write_file(OUTPUT_DIR, "updated_memory.txt", memory) - -if __name__ == "__main__": - main() diff --git a/verif/python/validate_config.py b/verif/python/validate_config.py index f69b657..14f494d 100755 --- a/verif/python/validate_config.py +++ b/verif/python/validate_config.py @@ -30,30 +30,30 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, f"({matrix_size_m}x{matrix_size_n}) and element width ({elem_width})") # Mode validation (based on config.mk) - if datamover_mode not in [0, 1, 2]: - errors.append(f"DATAMOVER_MODE ({datamover_mode}) must be 0 (copy), 1 (transpose), or 2 (CIM data layout conversion)") + if datamover_mode not in [0, 1, 2, 3]: + errors.append(f"DATAMOVER_MODE ({datamover_mode}) must be 0 (copy), 1 (transpose), 2 (CIM data layout conversion) or 3 (CIM layout transpose)") - if transp_mode not in [0, 1, 2, 4]: - errors.append(f"TRANSP_MODE ({transp_mode}) must be 0, 1, 2, or 4") + # if transp_mode not in [0, 1, 2, 4]: + # errors.append(f"TRANSP_MODE ({transp_mode}) must be 0, 1, 2, or 4") - # Mode consistency validation - if datamover_mode == 0 and transp_mode != 0: - warnings.append(f"Copy mode (DATAMOVER_MODE=0) typically uses TRANSP_MODE=0, but got {transp_mode}") + # # Mode consistency validation + # if datamover_mode == 0 and transp_mode != 0: + # warnings.append(f"Copy mode (DATAMOVER_MODE=0) typically uses TRANSP_MODE=0, but got {transp_mode}") - if datamover_mode == 1 and transp_mode == 0: - warnings.append(f"Transpose mode (DATAMOVER_MODE=1) typically uses TRANSP_MODE > 0, but got {transp_mode}") + if datamover_mode == 1 and transp_mode not in [1, 2, 4]: + errors.append(f"Transpose mode (DATAMOVER_MODE=1) requires TRANSP_MODE = [1,2,4] but got {transp_mode}") # CIM-specific validation - if datamover_mode == 2: + if datamover_mode in [2, 3]: if cim_mode not in [0, 1]: errors.append(f"CIM_MODE ({cim_mode}) must be 0 (row-major -> A-Layout) or 1 (row-major -> B-Layout)") - if cim_inner_dim < bandwidth_elems: - errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) must be greater than elements per bandwidth ({bandwidth_elems})") - if cim_outer_dim < bandwidth_elems: - errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) must be greater than elements per bandwidth ({bandwidth_elems})") - if cim_inner_dim > matrix_size_n: + if (cim_inner_dim % bandwidth_elems != 0) and (cim_mode == 0): + errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) must be a multiple of bandwidth ({bandwidth_elems})") + if (cim_outer_dim % bandwidth_elems != 0) and (cim_mode == 1): + errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) must be a multiple of bandwidth ({bandwidth_elems})") + if (cim_inner_dim > matrix_size_n) and (cim_mode == 0): errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) cannot be greater than matrix width ({matrix_size_n})") - if cim_outer_dim > matrix_size_m: + if (cim_outer_dim > matrix_size_m) and (cim_mode == 1): errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) cannot be greater than matrix height ({matrix_size_m})") # Memory requirements @@ -65,10 +65,10 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, errors.append(f"Memory size ({memory_size} words) insufficient for matrices " f"({total_memory_needed} words needed for {matrix_size_m}x{matrix_size_n} input+output)") - # # Matrix dimension alignment errors - # if matrix_size_n % bandwidth_elems != 0: - # errors.append(f"Matrix width ({matrix_size_n}) not aligned to bandwidth " - # f"({bandwidth_elems} elements)") + # Matrix dimension alignment errors + if matrix_size_n % bandwidth_elems != 0: + errors.append(f"Matrix width ({matrix_size_n}) not aligned to bandwidth " + f"({bandwidth_elems} elements)") # if matrix_size_m % bandwidth_elems != 0: # errors.append(f"Matrix height ({matrix_size_m}) not aligned to bandwidth " @@ -138,13 +138,13 @@ def main(): # Print computed values bandwidth_elems = args.bandwidth // args.elem_width num_elem_word = args.word_width // args.elem_width - matrix_words = (args.matrix_size_m * args.matrix_size_n * args.elem_width + args.word_width - 1) // args.word_width + matrix_words = (args.matrix_size_m * args.matrix_size_n) // num_elem_word print(f"\nComputed values:") print(f" Elements per bandwidth: {bandwidth_elems}") print(f" Elements per word: {num_elem_word}") print(f" Matrix memory usage: {matrix_words} words ({matrix_words * 2} total)") - print(f" Memory utilization: {(matrix_words * 2 * 100) // args.memory_size}%") + print(f" Memory utilization: {(matrix_words * 2 * 100) // args.memory_size}% ({matrix_words*2} / {args.memory_size})") return 0 diff --git a/verif/tb/tb_package.sv b/verif/tb/tb_package.sv index 61f6038..7a01151 100644 --- a/verif/tb/tb_package.sv +++ b/verif/tb/tb_package.sv @@ -190,7 +190,7 @@ package tb_package; status = 1; $display("MISMATCH at address %0d: Expected %h, Actual %h", start_addr + i, golden_data, read_data); end else begin - $display("MATCH at address %0d: %h", start_addr + i, read_data); + // $display("MATCH at address %0d: %h", start_addr + i, read_data); end end From 45322e0a243729daf4044b6005eb22a62190f6f5 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Tue, 6 Jan 2026 10:36:20 +0100 Subject: [PATCH 24/29] [rtl, tb, gm] Implemented partial tile support for copy mode --- Makefile | 3 ++ config.mk | 29 +++++++++++++----- rtl/datamover_engine.sv | 19 +++++++----- rtl/datamover_package.sv | 6 ++++ rtl/datamover_top.sv | 4 ++- verif/python/generate_stimuli.py | 49 +++++++++++++++++++------------ verif/python/validate_config.py | 21 +++++++------ verif/tb/tb_datamover_top_wrap.sv | 9 ++++++ 8 files changed, 95 insertions(+), 45 deletions(-) diff --git a/Makefile b/Makefile index 299e3a5..bc1a8be 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,9 @@ TESTBENCH_DEFINES += -DSTIM_MEM_SIZE=${STIM_MEM_SIZE} TESTBENCH_DEFINES += -DSTIM_TRANSP_MODE=${STIM_TRANSP_MODE} TESTBENCH_DEFINES += -DSTIM_TRANSP_LEN=${STIM_TRANSP_LEN} +TESTBENCH_DEFINES += -DSTIM_MATRIX_SIZE_M=${STIM_MATRIX_SIZE_M} +TESTBENCH_DEFINES += -DSTIM_MATRIX_SIZE_N=${STIM_MATRIX_SIZE_N} + TESTBENCH_DEFINES += -DBANDWIDTH=${BANDWIDTH} TESTBENCH_DEFINES += -DNUM_ELEM_WORD=${NUM_ELEM_WORD} TESTBENCH_DEFINES += -DELEM_WIDTH=${ELEM_WIDTH} diff --git a/config.mk b/config.mk index 27173a2..cbbb9e2 100644 --- a/config.mk +++ b/config.mk @@ -13,11 +13,11 @@ ####################### # Hardware configuration (can be overridden by presets or command line) -BANDWIDTH ?= 64 # in bits, multiple of WORD_WIDTH (512) -WORD_WIDTH ?= 32 # in bits, multiple of ELEM_WIDTH (64) +BANDWIDTH ?= 576 # in bits, multiple of WORD_WIDTH (512) +WORD_WIDTH ?= 64 # in bits, multiple of ELEM_WIDTH (64) ELEM_WIDTH ?= 8 # in bits (8) MEMORY_SIZE ?= 65536 # in words -MISALIGNED_ACCESSES ?= 0 +MISALIGNED_ACCESSES ?= 1 DATAMOVER_MODE ?= 0 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion, 3 = CIM data layout transpose TRANSP_MODE ?= 1 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted @@ -26,8 +26,8 @@ CIM_INNER_DIM ?= 64 # Inner dimension of the CIM accelerator (in elements): CIM_OUTER_DIM ?= 64 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros # Input matrix dimensions (in elements) -MATRIX_SIZE_M ?= 8 # Matrix height in elements -MATRIX_SIZE_N ?= 8 # Matrix width in elements +MATRIX_SIZE_M ?= 511 # Matrix height in elements +MATRIX_SIZE_N ?= 511 # Matrix width in elements WRITE_BASE_ADDR = $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Element-addressed @@ -37,14 +37,19 @@ BANDWIDTH_ALIGNED := $(shell echo $$(($(BANDWIDTH) - $(BANDWIDTH_REDUCTION)))) BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH_ALIGNED) / $(ELEM_WIDTH)))) # Number of elements per bandwidth NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of elements per word MATRIX_SIZE_TOT := $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Total number of elements in the matrix +MATRIX_MISALIGNED := $(shell echo $$(($(MATRIX_SIZE_TOT) % $(BANDWIDTH_ELEMS)))) # 1 if matrix size is not multiple of bandwidth elements +ifeq "$(strip $(MATRIX_MISALIGNED))" "0" # Matrix size aligned + TOTAL_ACCESSES := $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS)))) # Total number of memory accesses (words) for the matrix (floor division) +else # Matrix size misaligned + TOTAL_ACCESSES := $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS) + 1))) # Total number of memory accesses (words) for the matrix (+1 for misaligned access) +endif -# ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (4*32b) -# N (bandwidth) consecutive words are read/written in one transaction +# ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (bandwidth) ifeq "$(strip $(DATAMOVER_MODE))" "0" # Copy mode $(info Copy mode enabled) STIM_READ_BASE_ADDR ?= 0 # Element-addressed -STIM_READ_D0_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS)))) # [Nof accesses with bandwidth BW per D0-transfer ("row")] ToDo(cdurrer): not working for misaligned matrices +STIM_READ_D0_LENGTH ?= $(TOTAL_ACCESSES) # [Nof accesses with bandwidth BW per D0-transfer ("row")] ToDo(cdurrer): not working for misaligned matrices STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] STIM_READ_D1_LENGTH ?= 0 # Not used in copy mode STIM_READ_D1_STRIDE ?= 0 # Not used in copy mode @@ -55,6 +60,8 @@ STIM_READ_TOT_LENGTH ?= $(STIM_READ_D0_LENGTH) # [Total memory accesses STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= 0 STIM_TRANSP_LEN ?= 0 +STIM_MATRIX_SIZE_M ?= $(MATRIX_SIZE_M) +STIM_MATRIX_SIZE_N ?= $(MATRIX_SIZE_N) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) STIM_WRITE_D0_LENGTH ?= $(STIM_READ_D0_LENGTH) @@ -82,6 +89,8 @@ STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1 STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH +STIM_MATRIX_SIZE_M ?= $(MATRIX_SIZE_M) +STIM_MATRIX_SIZE_N ?= $(MATRIX_SIZE_N) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(BANDWIDTH_ELEMS) / $(TRANSP_MODE)))) # Transpose tile width corresponds to bandwidth @@ -109,6 +118,8 @@ STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(PARTIAL_MULT) * $(STIM_READ_D2_LENGTH STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= 0 STIM_TRANSP_LEN ?= 0 +STIM_MATRIX_SIZE_M ?= $(MATRIX_SIZE_M) +STIM_MATRIX_SIZE_N ?= $(MATRIX_SIZE_N) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(MATRIX_SIZE_M)))) @@ -139,6 +150,8 @@ STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS) STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= $(TRANSP_MODE) # transp_mode STIM_TRANSP_LEN ?= 0 +STIM_MATRIX_SIZE_M ?= $(MATRIX_SIZE_M) +STIM_MATRIX_SIZE_N ?= $(MATRIX_SIZE_N) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(BANDWIDTH_ELEMS) diff --git a/rtl/datamover_engine.sv b/rtl/datamover_engine.sv index f000e34..543c448 100644 --- a/rtl/datamover_engine.sv +++ b/rtl/datamover_engine.sv @@ -16,11 +16,6 @@ * Sergio Mazzola */ -// ToDo(cdurrer): TEMP -parameter int unsigned MATRIX_SIZE_TOT = 25; -parameter int unsigned REMAINING_ELEMS = 1; - - module datamover_engine import hwpe_stream_package::*; import hci_package::*; @@ -52,6 +47,12 @@ module datamover_engine // number of elements (in the full bandwidth, not a single bank word) localparam NB_ELEMENTS = BANDWIDTH_ALIGNED / ELEM_WIDTH; + logic [23:0] matrix_tot_size; + assign matrix_tot_size = ctrl_i.matrix_size_m * ctrl_i.matrix_size_n; // ToDo(cdurrer): additional MUL, problem? + logic [$clog2(NB_ELEMENTS)-1:0] remaining_elems; + assign remaining_elems = matrix_tot_size % NB_ELEMENTS; + logic [11:0] nof_accesses; + assign nof_accesses = (matrix_tot_size / NB_ELEMENTS) + ((remaining_elems != 0) ? 1 : 0); // Type def and internal signals typedef enum logic { WRITE, READ } datamover_engine_fsm_t; @@ -144,8 +145,9 @@ module datamover_engine .push_i ( data_out_prefifo ), .pop_o ( data_out ) ); - // assign data_out_prefifo.strb = '1; // FIXME for leftovers ToDo(cdurrer): use for partial tiles - assign data_out_prefifo.strb = '1; //(tot_cnt_q >= 7-1) ? (1 << REMAINING_ELEMS) - 1 : '1; + + // handle last transfer with partial strobe if misaligned + assign data_out_prefifo.strb = ((tot_cnt_q >= nof_accesses-1) && (remaining_elems != 0)) ? (1 << remaining_elems) - 1 : '1; assign data_out_prefifo.data = data_out_unrolled; assign data_out_prefifo.valid = data_out_valid; @@ -170,7 +172,8 @@ module datamover_engine assign cnt_d = cnt_q < ctrl_i.transp_len-ctrl_i.transp_stride ? cnt_q+ctrl_i.transp_stride : '0; assign cnt_en = fsm_q == WRITE ? data_in_valid & data_in_ready : data_out_valid & data_out_ready; - assign tot_cnt_d = (tot_cnt_q < MATRIX_SIZE_TOT) && (data_out_prefifo.valid) ? tot_cnt_q + 1 : tot_cnt_q; + // count total number of write accesses + assign tot_cnt_d = (tot_cnt_q < matrix_tot_size) && (data_out_prefifo.valid) ? tot_cnt_q + 1 : tot_cnt_q; // "Smart shifting": this set of combinational blocks shifts data_in_unrolled // appropriately, depending on the configuration. diff --git a/rtl/datamover_package.sv b/rtl/datamover_package.sv index 69c33af..9941304 100644 --- a/rtl/datamover_package.sv +++ b/rtl/datamover_package.sv @@ -36,6 +36,8 @@ package datamover_package; transp_mode_e transp_mode; logic [$clog2(MAX_BANDWIDTH/8):0] transp_len; logic [2:0] transp_stride; // 1, 2, or 4 + logic [11:0] matrix_size_m; + logic [11:0] matrix_size_n; } ctrl_engine_t; parameter int unsigned HWPE_REGISTER_OFFS = 32'h00; // Standard HWPE register offset @@ -67,5 +69,9 @@ package datamover_package; parameter int unsigned DATAMOVER_REG_OUT_D2_STRIDE = 32'h24; // Output dimension 2 stride parameter int unsigned DATAMOVER_REG_TRANSP_MODE = 32'h28; // Transposition mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) // Leftover: [31:16], if 0 then no leftover + parameter int unsigned DATAMOVER_REG_MATRIX_SIZE = 32'h2C; // [31:24] unused; [23:12] matrix_size_n; [11:0] matrix_size_m + + // Note: increase N_IO_REGS in datamover_top.sv when adding new registers here! + endpackage diff --git a/rtl/datamover_top.sv b/rtl/datamover_top.sv index 9863b85..9166074 100644 --- a/rtl/datamover_top.sv +++ b/rtl/datamover_top.sv @@ -148,7 +148,7 @@ module datamover_top .REGFILE_SCM ( 0 ), .N_CORES ( N_CORES ), .N_CONTEXT ( N_CONTEXT ), - .N_IO_REGS ( 11 ), + .N_IO_REGS ( 12 ), .N_GENERIC_REGS ( 8 ), .ID_WIDTH ( ID ) ) i_slave ( @@ -241,6 +241,8 @@ module datamover_top engine_ctrl.transp_stride = reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b000 ? 1 : reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b001 ? 1 : reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b010 ? 2 : 4; + engine_ctrl.matrix_size_m = reg_file.hwpe_params[DATAMOVER_REG_MATRIX_SIZE >> 2][11:0]; + engine_ctrl.matrix_size_n = reg_file.hwpe_params[DATAMOVER_REG_MATRIX_SIZE >> 2][23:12]; if(reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16] == '0) begin // no leftover engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; // ToDo(cdurrer): why? end diff --git a/verif/python/generate_stimuli.py b/verif/python/generate_stimuli.py index 69b086b..a0d961a 100644 --- a/verif/python/generate_stimuli.py +++ b/verif/python/generate_stimuli.py @@ -3,10 +3,7 @@ import os import math -RANDOM_STIMULI = False # If False, counting stimuli are generated in a counting fashion -WORD_ALIGNED = False # If True, matrices are aligned to word boundaries by zero-padding - -# ToDo(cdurrer): small matrices (N < BW) not working +RANDOM_STIMULI = True # If False, stimuli are generated in a counting fashion def extract_elements_from_word(word, word_width, elem_width): """Extract elements from a word based on the specified widths.""" @@ -78,6 +75,30 @@ def matrix_to_hex_words(matrix, elem_width, word_width): hex_words.append(hex_word) return hex_words +def write_element_to_memory(element, memory, elem_width, word_width, address): + """Write a single element back to memory at specified address.""" + elems_per_word = word_width // elem_width + word_index = address // elems_per_word + elem_index = address % elems_per_word + existing_word = memory[word_index] + existing_elements = extract_elements_from_word(existing_word, word_width, elem_width) + existing_elements[elem_index] = element + hex_word = pack_elements_to_word(existing_elements, elem_width, word_width) + memory[word_index] = hex_word + return memory + +def write_matrix_to_memory(matrix, memory, elem_width, word_width, write_base_addr): + """Write output matrix back to memory at specified base address.""" + elems_per_word = word_width // elem_width + matrix_flat = sum(matrix, []) + matrix_elems = len(matrix_flat) + for i in range(matrix_elems): + address = write_base_addr + i + element = matrix_flat[i] + memory = write_element_to_memory(element, memory, elem_width, word_width, address) + return memory + +# ToDo(cdurrer): obsolete, delete? def matrix_to_hex_words_word_aligned(matrix, elem_width, word_width): """Convert a matrix of elements to a list of hex words - aligned to word boundaries by zero-padding.""" elems_per_word = word_width // elem_width @@ -342,23 +363,13 @@ def main(): # Compare input and output matrix: equality check if (output_matrix == input_matrix): - print("Output matrix matches input matrix.") - - # # Convert output matrix back to words - if (WORD_ALIGNED): - output_hex_words = matrix_to_hex_words_word_aligned(output_matrix, ELEM_WIDTH, WORD_WIDTH) - else: - output_hex_words = matrix_to_hex_words(output_matrix, ELEM_WIDTH, WORD_WIDTH) + print("Output matrix equals input matrix.") - # print(f"\nOutput Matrix as Hex Words:") - # for i, word in enumerate(output_hex_words): - # print(f"Word {i}: {word}") + memory = write_matrix_to_memory(output_matrix, memory, ELEM_WIDTH, WORD_WIDTH, WRITE_BASE_ADDR) - # Write back output matrix to memory at WRITE_BASE_ADDR - for i, word in enumerate(output_hex_words): - memory[(WRITE_BASE_ADDR // (WORD_WIDTH // 8) + i)] = word - # print(f"Writing word {word} to memory address [Byte-address] {WRITE_BASE_ADDR + i * (WORD_WIDTH // 8)}") - # print(f"Writing word {word} to memory address [Word-address] {WRITE_BASE_ADDR + i}") + # print("\nFinal Memory Content:") + # for i, word in enumerate(memory): + # print(f"Memory[{i}]: {word}") write_file(OUTPUT_DIR, "updated_memory.txt", memory) diff --git a/verif/python/validate_config.py b/verif/python/validate_config.py index 14f494d..afce8b7 100755 --- a/verif/python/validate_config.py +++ b/verif/python/validate_config.py @@ -7,20 +7,22 @@ import sys import argparse -def validate_config(bandwidth, word_width, elem_width, memory_size, +def validate_config(bandwidth, word_width, elem_width, memory_size, misaligned_accesses, datamover_mode, transp_mode, cim_mode, cim_inner_dim, cim_outer_dim, matrix_size_m, matrix_size_n): """Validate configuration parameters""" errors = [] warnings = [] + bandwidth_aligned = bandwidth - (word_width if misaligned_accesses else 0) + # Computed values - bandwidth_elems = bandwidth // elem_width + bandwidth_elems = bandwidth_aligned // elem_width num_elem_word = word_width // elem_width # Basic parameter validation - if bandwidth % word_width != 0: - errors.append(f"BANDWIDTH ({bandwidth}) must be divisible by WORD_WIDTH ({word_width})") + if bandwidth_aligned % word_width != 0: + errors.append(f"BANDWIDTH_ALIGNED ({bandwidth_aligned}) must be divisible by WORD_WIDTH ({word_width})") if word_width % elem_width != 0: errors.append(f"WORD_WIDTH ({word_width}) must be divisible by ELEM_WIDTH ({elem_width})") @@ -66,9 +68,9 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, f"({total_memory_needed} words needed for {matrix_size_m}x{matrix_size_n} input+output)") # Matrix dimension alignment errors - if matrix_size_n % bandwidth_elems != 0: - errors.append(f"Matrix width ({matrix_size_n}) not aligned to bandwidth " - f"({bandwidth_elems} elements)") + # if matrix_size_n % bandwidth_elems != 0: + # errors.append(f"Matrix width ({matrix_size_n}) not aligned to bandwidth " + # f"({bandwidth_elems} elements)") # if matrix_size_m % bandwidth_elems != 0: # errors.append(f"Matrix height ({matrix_size_m}) not aligned to bandwidth " @@ -101,7 +103,7 @@ def main(): errors, warnings = validate_config( args.bandwidth, args.word_width, args.elem_width, args.memory_size, - args.datamover_mode, args.transp_mode, args.cim_mode, + args.misaligned_accesses, args.datamover_mode, args.transp_mode, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, args.matrix_size_m, args.matrix_size_n ) @@ -136,7 +138,8 @@ def main(): print(f" CIM_OUTER_DIM: {args.cim_outer_dim}") # Print computed values - bandwidth_elems = args.bandwidth // args.elem_width + bandwidth_aligned = args.bandwidth - (args.word_width if args.misaligned_accesses else 0) + bandwidth_elems = bandwidth_aligned // args.elem_width num_elem_word = args.word_width // args.elem_width matrix_words = (args.matrix_size_m * args.matrix_size_n) // num_elem_word diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index 096477c..cb8b333 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -63,6 +63,8 @@ import tb_package::*; logic [2:0] transp_mode; logic [15:0] transp_len; + logic [11:0] matrix_size_m; + logic [11:0] matrix_size_n; // Performs one entire clock cycle. task cycle; @@ -120,6 +122,8 @@ import tb_package::*; assign transp_mode = `STIM_TRANSP_MODE; assign transp_len = `STIM_TRANSP_LEN; + assign matrix_size_m = `STIM_MATRIX_SIZE_M; + assign matrix_size_n = `STIM_MATRIX_SIZE_N; datamover_top_wrap #( @@ -226,6 +230,7 @@ import tb_package::*; logic [31:0] len0_reg; logic [31:0] len1_reg; logic [31:0] transp_mode_reg; + logic [31:0] matrix_size_reg; $info("Start execution...\n"); @@ -258,6 +263,8 @@ import tb_package::*; len0_reg = {read_addr.d1_length[7:0], read_addr.d0_length[11:0], read_addr.tot_length[11:0]}; len1_reg = {4'b0, read_addr.d1_length[11:8], write_addr.d1_length[11:0], write_addr.d0_length[11:0]}; transp_mode_reg = {transp_len, 13'b0, transp_mode}; // ToDo(cdurrer): Leftover = transp_len??? + matrix_size_reg = {8'b0, matrix_size_n[11:0], matrix_size_m[11:0]}; + // Make sure tot_length is the same for read and write assert (read_addr.tot_length == write_addr.tot_length) else $fatal("Read and write total lengths do not match!"); @@ -275,6 +282,8 @@ import tb_package::*; // Transposition mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) periph_write(datamover_package::DATAMOVER_REG_TRANSP_MODE, datamover_package::DATAMOVER_REGISTER_OFFS, transp_mode_reg, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_MATRIX_SIZE, datamover_package::DATAMOVER_REGISTER_OFFS, matrix_size_reg, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_COMMIT_AND_TRIGGER, datamover_package::HWPE_REGISTER_OFFS, 32'h0, clk_i, periph_bus); while(status == 32'h00) From 3afb7564803af3fc2950fd1dcc9d5cee59de45aa Mon Sep 17 00:00:00 2001 From: cdurrer Date: Tue, 6 Jan 2026 18:32:50 +0100 Subject: [PATCH 25/29] Renamed matrix_size to matrix_dim [rtl,tb] Refactored configuration registers to save init cycles and increase bitwidth. Added configurability of d3, d4 and dim_enable. [rtl] Fixed error in matrix_dim assignment, switched transp_mode enum to make it more readable (TRANSP_NONE now corresponds to 2'b00) [rtl,sw] small fixes and misalignment handling additions, added high-level (numpy) and microarchitectural golden models --- CONFIG_USAGE.md | 35 +- Makefile | 26 +- README.md | 4 + config.mk | 163 +++++++--- config_presets.mk | 52 +-- rtl/datamover_engine.sv | 6 +- rtl/datamover_package.sv | 35 +- rtl/datamover_top.sv | 67 ++-- test/hal_datamover.h | 2 +- verif/python/datamover_golden_model_numpy.py | 61 ++++ .../datamover_microarchitectural_model.py | 299 ++++++++++++++++++ verif/python/generate_stimuli.py | 86 +++-- verif/python/validate_config.py | 34 +- verif/tb/tb_datamover_top_wrap.sv | 81 +++-- 14 files changed, 713 insertions(+), 238 deletions(-) create mode 100644 verif/python/datamover_golden_model_numpy.py create mode 100644 verif/python/datamover_microarchitectural_model.py diff --git a/CONFIG_USAGE.md b/CONFIG_USAGE.md index c2ff9c7..607eeaa 100644 --- a/CONFIG_USAGE.md +++ b/CONFIG_USAGE.md @@ -75,7 +75,7 @@ make sim CONFIG_PRESET=small-matrix TRANSP_MODE=2 make sim CONFIG_PRESET=medium-matrix ELEM_WIDTH=16 # Override matrix dimensions -make sim CONFIG_PRESET=transpose-test MATRIX_SIZE_M=64 MATRIX_SIZE_N=64 +make sim CONFIG_PRESET=transpose-test MATRIX_DIM_M=64 MATRIX_DIM_N=64 ``` ### Custom Configuration @@ -84,7 +84,7 @@ Use completely custom parameters: ```bash # Custom configuration via command line -make sim CONFIG_PRESET=custom BANDWIDTH=512 ELEM_WIDTH=16 MATRIX_SIZE_M=128 MATRIX_SIZE_N=128 +make sim CONFIG_PRESET=custom BANDWIDTH=512 ELEM_WIDTH=16 MATRIX_DIM_M=128 MATRIX_DIM_N=128 # Or edit config.mk for persistent custom settings make sim CONFIG_PRESET=custom @@ -153,11 +153,26 @@ The datamover supports three main operation modes: | `CIM_OUTER_DIM` | 16,32,64,... | CIM outer dimension in elements | | `ELEM_WIDTH` | 8 | Element width in bits | | `BANDWIDTH` | 64,128,256,512,1024 | Memory bandwidth in bits | -| `MATRIX_SIZE_M` | Any | Matrix height in elements | -| `MATRIX_SIZE_N` | Any | Matrix width in elements | +| `MATRIX_DIM_M` | Any | Matrix height in elements | +| `MATRIX_DIM_N` | Any | Matrix width in elements | | `MEMORY_SIZE` | Any | Available memory in words | | `WORD_WIDTH` | 16,32,64 | Word width in bits (typically 32) | +### Testbench Stimulus Parameters + +| Parameter | Description | +|-----------|-------------| +| `STIM_READ_BASE_ADDR` / `STIM_WRITE_BASE_ADDR` | Start address for read/write bursts (element-addressed) | +| `STIM_*_D0_LENGTH` / `STIM_*_D0_STRIDE` | Inner dimension length and stride (elements) | +| `STIM_*_D1_LENGTH` / `STIM_*_D1_STRIDE` | Second dimension length and stride | +| `STIM_*_D2_LENGTH` / `STIM_*_D2_STRIDE` | Optional third dimension length/stride (0 when unused) | +| `STIM_*_D3_LENGTH` / `STIM_*_D3_STRIDE` | Optional fourth dimension length/stride (0 when unused) | +| `STIM_*_D4_STRIDE` | Optional stride for a fifth dimension (0 when unused) | +| `STIM_*_TOT_LENGTH` | Total number of accesses for the transfer | +| `STIM_*_DIM_ENABLE` | 4-bit mask enabling address-generation dimensions (d1..d4) -> d0 always active | +| `STIM_MEM_SIZE` | Number of words in the testbench memory | +| `STIM_TRANSP_MODE` | Transpose mode encoded for the datamover: 1,2 or 4 elements | + ## Built-in Test Targets The system provides several built-in test targets for comprehensive testing: @@ -190,8 +205,8 @@ ifeq ($(CONFIG_PRESET),my-test) ELEM_WIDTH = 16 MEMORY_SIZE = 32768 TRANSP_MODE = 4 - MATRIX_SIZE_M = 64 - MATRIX_SIZE_N = 32 + MATRIX_DIM_M = 64 + MATRIX_DIM_N = 32 CONFIG_DESC = "Custom test for specific use case" endif ``` @@ -251,7 +266,7 @@ test-custom-suite: @echo "Testing custom configuration suite..." $(MAKE) sim CONFIG_PRESET=small-matrix TRANSP_MODE=2 $(MAKE) sim CONFIG_PRESET=medium-matrix ELEM_WIDTH=16 - $(MAKE) sim CONFIG_PRESET=custom BANDWIDTH=1024 MATRIX_SIZE_M=64 MATRIX_SIZE_N=64 + $(MAKE) sim CONFIG_PRESET=custom BANDWIDTH=1024 MATRIX_DIM_M=64 MATRIX_DIM_N=64 ``` ### Configuration Validation @@ -336,8 +351,8 @@ make test-cim-grid make sim CONFIG_PRESET=custom \ BANDWIDTH=1024 \ ELEM_WIDTH=32 \ - MATRIX_SIZE_M=256 \ - MATRIX_SIZE_N=128 \ + MATRIX_DIM_M=256 \ + MATRIX_DIM_N=128 \ TRANSP_MODE=4 ``` @@ -369,7 +384,7 @@ The configuration system consists of these key files: 3. **Bandwidth alignment warnings**: Matrix dimensions not aligned to bandwidth ```bash # Adjust matrix dimensions to be multiples of BANDWIDTH/ELEM_WIDTH - make sim MATRIX_SIZE_M=32 MATRIX_SIZE_N=32 # Use aligned dimensions + make sim MATRIX_DIM_M=32 MATRIX_DIM_N=32 # Use aligned dimensions ``` 4. **Configuration not taking effect**: Check parameter precedence diff --git a/Makefile b/Makefile index bc1a8be..7a9c7df 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,12 @@ TESTBENCH_DEFINES += -DSTIM_READ_D0_LENGTH=${STIM_READ_D0_LENGTH} TESTBENCH_DEFINES += -DSTIM_READ_D1_STRIDE=${STIM_READ_D1_STRIDE} TESTBENCH_DEFINES += -DSTIM_READ_D1_LENGTH=${STIM_READ_D1_LENGTH} TESTBENCH_DEFINES += -DSTIM_READ_D2_STRIDE=${STIM_READ_D2_STRIDE} +TESTBENCH_DEFINES += -DSTIM_READ_D2_LENGTH=${STIM_READ_D2_LENGTH} +TESTBENCH_DEFINES += -DSTIM_READ_D3_STRIDE=${STIM_READ_D3_STRIDE} +TESTBENCH_DEFINES += -DSTIM_READ_D3_LENGTH=${STIM_READ_D3_LENGTH} +TESTBENCH_DEFINES += -DSTIM_READ_D4_STRIDE=${STIM_READ_D4_STRIDE} TESTBENCH_DEFINES += -DSTIM_READ_TOT_LENGTH=${STIM_READ_TOT_LENGTH} +TESTBENCH_DEFINES += -DSTIM_READ_DIM_ENABLE=${STIM_READ_DIM_ENABLE} TESTBENCH_DEFINES += -DSTIM_WRITE_BASE_ADDR=${STIM_WRITE_BASE_ADDR} TESTBENCH_DEFINES += -DSTIM_WRITE_D0_STRIDE=${STIM_WRITE_D0_STRIDE} @@ -47,15 +52,20 @@ TESTBENCH_DEFINES += -DSTIM_WRITE_D0_LENGTH=${STIM_WRITE_D0_LENGTH} TESTBENCH_DEFINES += -DSTIM_WRITE_D1_STRIDE=${STIM_WRITE_D1_STRIDE} TESTBENCH_DEFINES += -DSTIM_WRITE_D1_LENGTH=${STIM_WRITE_D1_LENGTH} TESTBENCH_DEFINES += -DSTIM_WRITE_D2_STRIDE=${STIM_WRITE_D2_STRIDE} +TESTBENCH_DEFINES += -DSTIM_WRITE_D2_LENGTH=${STIM_WRITE_D2_LENGTH} +TESTBENCH_DEFINES += -DSTIM_WRITE_D3_STRIDE=${STIM_WRITE_D3_STRIDE} +TESTBENCH_DEFINES += -DSTIM_WRITE_D3_LENGTH=${STIM_WRITE_D3_LENGTH} +TESTBENCH_DEFINES += -DSTIM_WRITE_D4_STRIDE=${STIM_WRITE_D4_STRIDE} TESTBENCH_DEFINES += -DSTIM_WRITE_TOT_LENGTH=${STIM_WRITE_TOT_LENGTH} +TESTBENCH_DEFINES += -DSTIM_WRITE_DIM_ENABLE=${STIM_WRITE_DIM_ENABLE} TESTBENCH_DEFINES += -DSTIM_MEM_SIZE=${STIM_MEM_SIZE} TESTBENCH_DEFINES += -DSTIM_TRANSP_MODE=${STIM_TRANSP_MODE} -TESTBENCH_DEFINES += -DSTIM_TRANSP_LEN=${STIM_TRANSP_LEN} +# TESTBENCH_DEFINES += -DSTIM_TRANSP_LEN=${STIM_TRANSP_LEN} -TESTBENCH_DEFINES += -DSTIM_MATRIX_SIZE_M=${STIM_MATRIX_SIZE_M} -TESTBENCH_DEFINES += -DSTIM_MATRIX_SIZE_N=${STIM_MATRIX_SIZE_N} +TESTBENCH_DEFINES += -DSTIM_MATRIX_DIM_M=${STIM_MATRIX_DIM_M} +TESTBENCH_DEFINES += -DSTIM_MATRIX_DIM_N=${STIM_MATRIX_DIM_N} TESTBENCH_DEFINES += -DBANDWIDTH=${BANDWIDTH} TESTBENCH_DEFINES += -DNUM_ELEM_WORD=${NUM_ELEM_WORD} @@ -91,7 +101,7 @@ help: @echo "Usage examples:" @echo " make sim CONFIG_PRESET=small-matrix" @echo " make sim CONFIG_PRESET=transpose-test TRANSP_MODE=2" - @echo " make sim MATRIX_SIZE_M=64 MATRIX_SIZE_N=32" + @echo " make sim MATRIX_DIM_M=64 MATRIX_DIM_N=32" @echo "" @echo "Test targets:" @echo " make test-all-presets : Test all presets (detailed reporting)" @@ -299,8 +309,8 @@ validate-config: --cim_mode $(CIM_MODE) \ --cim_inner_dim $(CIM_INNER_DIM) \ --cim_outer_dim $(CIM_OUTER_DIM) \ - --matrix_size_m $(MATRIX_SIZE_M) \ - --matrix_size_n $(MATRIX_SIZE_N) + --matrix_dim_m $(MATRIX_DIM_M) \ + --matrix_dim_n $(MATRIX_DIM_N) clean-sim: rm -rf $(SIM_PATH)/work @@ -335,8 +345,8 @@ stimuli: clean-stimuli validate-config --cim_mode $(CIM_MODE) \ --cim_inner_dim $(CIM_INNER_DIM) \ --cim_outer_dim $(CIM_OUTER_DIM) \ - --matrix_size_m $(MATRIX_SIZE_M) \ - --matrix_size_n $(MATRIX_SIZE_N) \ + --matrix_dim_m $(MATRIX_DIM_M) \ + --matrix_dim_n $(MATRIX_DIM_N) \ --output_dir "verif/python/generated" # Bender diff --git a/README.md b/README.md index bfd54ae..b8e4566 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,11 @@ The following parameters are used to generate the testbench stimuli and to confi `STIM_*_BASE_ADDR` = start address of the read/write access bursts (element-addressed) `STIM_*_LENGTH` = number of read/write accesses for the d0/d1 dimensions and in total (it is not an address offset!) `STIM_*_STRIDE` = stride between element across dimensions d0/d1 (element-addressed, e.g., stride d1 would be the distance in an element-addressed offset between A[row=0][col=0] and A[row=1][col=0]) +`STIM_*_D2/D3_LENGTH` = optional higher-dimension lengths when using multi-dimensional tiling (0 means unused) +`STIM_*_D2/D3_STRIDE` = stride for the higher dimensions (element-addressed) +`STIM_*_D4_STRIDE` = optional fourth-dimension stride (element-addressed) `STIM_MEM_SIZE` = number of words of the testbench memory +`STIM_*_DIM_ENABLE` = 4-bit mask enabling address generation dimensions (d0..d3) `STIM_TRANSP_MODE` = transposition mode to configure for the datamover (`3'b000` = none, `3'b001` = 1 elem, `3'b010` = 2 elem, `3'b100` = 4 elem) `STIM_TRANSP_LEN` = transposition length (if set to 0: transp_len = BANDWIDTH_ALIGNED / ELEM_WIDTH) diff --git a/config.mk b/config.mk index cbbb9e2..e810d2e 100644 --- a/config.mk +++ b/config.mk @@ -13,63 +13,87 @@ ####################### # Hardware configuration (can be overridden by presets or command line) -BANDWIDTH ?= 576 # in bits, multiple of WORD_WIDTH (512) +BANDWIDTH ?= 512 # in bits, multiple of WORD_WIDTH (512) WORD_WIDTH ?= 64 # in bits, multiple of ELEM_WIDTH (64) ELEM_WIDTH ?= 8 # in bits (8) MEMORY_SIZE ?= 65536 # in words -MISALIGNED_ACCESSES ?= 1 +MISALIGNED_ACCESSES ?= 0 -DATAMOVER_MODE ?= 0 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion, 3 = CIM data layout transpose +DATAMOVER_MODE ?= 1 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion, 3 = CIM data layout transpose TRANSP_MODE ?= 1 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> A-Layout, 1: row-major -> B-Layout CIM_INNER_DIM ?= 64 # Inner dimension of the CIM accelerator (in elements): 64 for 64x8 CIM macro CIM_OUTER_DIM ?= 64 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros # Input matrix dimensions (in elements) -MATRIX_SIZE_M ?= 511 # Matrix height in elements -MATRIX_SIZE_N ?= 511 # Matrix width in elements +MATRIX_DIM_M ?= 64 # Matrix height in elements +MATRIX_DIM_N ?= 64 # Matrix width in elements -WRITE_BASE_ADDR = $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Element-addressed +READ_BASE_ADDR = 0 # Derived constants from basic parameters BANDWIDTH_REDUCTION := $(shell echo $$(($(MISALIGNED_ACCESSES) * $(WORD_WIDTH)))) # in bits -BANDWIDTH_ALIGNED := $(shell echo $$(($(BANDWIDTH) - $(BANDWIDTH_REDUCTION)))) # in bytes -BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH_ALIGNED) / $(ELEM_WIDTH)))) # Number of elements per bandwidth -NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of elements per word -MATRIX_SIZE_TOT := $(shell echo $$(($(MATRIX_SIZE_M) * $(MATRIX_SIZE_N)))) # Total number of elements in the matrix -MATRIX_MISALIGNED := $(shell echo $$(($(MATRIX_SIZE_TOT) % $(BANDWIDTH_ELEMS)))) # 1 if matrix size is not multiple of bandwidth elements +BANDWIDTH_ALIGNED := $(shell echo $$(($(BANDWIDTH) - $(BANDWIDTH_REDUCTION)))) # in bytes +BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH_ALIGNED) / $(ELEM_WIDTH)))) # Number of elements per bandwidth +NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of elements per word +MATRIX_SIZE_TOT := $(shell echo $$(($(MATRIX_DIM_M) * $(MATRIX_DIM_N)))) # Total number of elements in the matrix +MATRIX_MISALIGNED := $(shell echo $$(($(MATRIX_SIZE_TOT) % $(BANDWIDTH_ELEMS)))) # 1 if matrix size is not multiple of bandwidth elements ifeq "$(strip $(MATRIX_MISALIGNED))" "0" # Matrix size aligned TOTAL_ACCESSES := $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS)))) # Total number of memory accesses (words) for the matrix (floor division) else # Matrix size misaligned TOTAL_ACCESSES := $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS) + 1))) # Total number of memory accesses (words) for the matrix (+1 for misaligned access) endif +WRITE_BASE_ADDR = $(shell echo $$(($(READ_BASE_ADDR) + $(MATRIX_SIZE_TOT)))) # Element-addressed + +# Align matrix dimensions to bandwidth for transposition (fill elem_matrix) +MATRIX_DIM_M_MOD := $(shell echo $$(( $(MATRIX_DIM_M) % $(BANDWIDTH_ELEMS) ))) +MATRIX_DIM_N_MOD := $(shell echo $$(( $(MATRIX_DIM_N) % $(BANDWIDTH_ELEMS) ))) +ifeq ($(MATRIX_DIM_M_MOD),0) + MATRIX_DIM_M_ALIGNED := $(MATRIX_DIM_M) +else + MATRIX_DIM_M_ALIGNED := $(shell echo $$(($(MATRIX_DIM_M) + $(BANDWIDTH_ELEMS) - $(MATRIX_DIM_M_MOD)))) # Align M dimension to bandwidth +endif +ifeq ($(MATRIX_DIM_N_MOD),0) + MATRIX_DIM_N_ALIGNED := $(MATRIX_DIM_N) +else + MATRIX_DIM_N_ALIGNED := $(shell echo $$(($(MATRIX_DIM_N) + $(BANDWIDTH_ELEMS) - $(MATRIX_DIM_N_MOD)))) # Align N dimension to bandwidth +endif # ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (bandwidth) ifeq "$(strip $(DATAMOVER_MODE))" "0" # Copy mode $(info Copy mode enabled) -STIM_READ_BASE_ADDR ?= 0 # Element-addressed -STIM_READ_D0_LENGTH ?= $(TOTAL_ACCESSES) # [Nof accesses with bandwidth BW per D0-transfer ("row")] ToDo(cdurrer): not working for misaligned matrices +STIM_READ_BASE_ADDR ?= $(READ_BASE_ADDR) # Element-addressed +STIM_READ_D0_LENGTH ?= $(TOTAL_ACCESSES) # [Nof accesses with bandwidth BW per D0-transfer ("row")] ToDo(cdurrer): not working for misaligned matrices STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -STIM_READ_D1_LENGTH ?= 0 # Not used in copy mode -STIM_READ_D1_STRIDE ?= 0 # Not used in copy mode -STIM_READ_D2_LENGTH ?= 0 # Not used in copy mode -STIM_READ_D2_STRIDE ?= 0 # Not used in copy mode +STIM_READ_D1_LENGTH ?= 0 +STIM_READ_D1_STRIDE ?= 0 +STIM_READ_D2_LENGTH ?= 0 +STIM_READ_D2_STRIDE ?= 0 +STIM_READ_D3_LENGTH ?= 0 +STIM_READ_D3_STRIDE ?= 0 +STIM_READ_D4_STRIDE ?= 0 STIM_READ_TOT_LENGTH ?= $(STIM_READ_D0_LENGTH) # [Total memory accesses] +STIM_READ_DIM_ENABLE ?= "4'b0000" STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= 0 -STIM_TRANSP_LEN ?= 0 -STIM_MATRIX_SIZE_M ?= $(MATRIX_SIZE_M) -STIM_MATRIX_SIZE_N ?= $(MATRIX_SIZE_N) +# STIM_TRANSP_LEN ?= 0 +STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) +STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) STIM_WRITE_D0_LENGTH ?= $(STIM_READ_D0_LENGTH) STIM_WRITE_D0_STRIDE ?= $(STIM_READ_D0_STRIDE) STIM_WRITE_D1_LENGTH ?= 0 STIM_WRITE_D1_STRIDE ?= 0 +STIM_WRITE_D2_LENGTH ?= 0 STIM_WRITE_D2_STRIDE ?= 0 +STIM_WRITE_D3_LENGTH ?= 0 +STIM_WRITE_D3_STRIDE ?= 0 +STIM_WRITE_D4_STRIDE ?= 0 STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) +STIM_WRITE_DIM_ENABLE ?= "4'b0000" else ifeq "$(strip $(DATAMOVER_MODE))" "1" # Transpose mode $(info Transpose mode $(TRANSP_MODE) enabled) @@ -77,57 +101,75 @@ ifneq ($(filter 1 2 4,$(strip $(TRANSP_MODE))), $(strip $(TRANSP_MODE))) $(error Invalid TRANSP_MODE $(TRANSP_MODE): must be 1, 2, or 4) endif -STIM_READ_BASE_ADDR ?= 0 # Element-addressed -STIM_READ_D0_LENGTH ?= $(MATRIX_SIZE_M) # [Nof accesses with bandwidth BW per D0-transfer ("row")] -STIM_READ_D0_STRIDE ?= $(MATRIX_SIZE_N) # [Elements] -STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(BANDWIDTH_ELEMS)))) # [Number of full D0-transfers ("rows")] +STIM_READ_BASE_ADDR ?= $(READ_BASE_ADDR) # Element-addressed +STIM_READ_D0_LENGTH ?= $(MATRIX_DIM_M_ALIGNED) # [Nof accesses with bandwidth BW per D0-transfer ("row")] +STIM_READ_D0_STRIDE ?= $(MATRIX_DIM_N) # [Elements] +STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_DIM_N_ALIGNED) / $(BANDWIDTH_ELEMS)))) # [Number of full D0-transfers ("rows")] STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -> manually compute "next row" stride -STIM_READ_D2_LENGTH ?= 0 # Not used in transpose mode -STIM_READ_D2_STRIDE ?= 0 # Not used in transpose mode -STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] +STIM_READ_D2_LENGTH ?= 0 +STIM_READ_D2_STRIDE ?= 0 +STIM_READ_D3_LENGTH ?= 0 +STIM_READ_D3_STRIDE ?= 0 +STIM_READ_D4_STRIDE ?= 0 +STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] TODO(cdurrer): FIX +STIM_READ_DIM_ENABLE ?= "4'b0001" STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted -STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH -STIM_MATRIX_SIZE_M ?= $(MATRIX_SIZE_M) -STIM_MATRIX_SIZE_N ?= $(MATRIX_SIZE_N) +# STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH +STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) # Actual (non-aligned) matrix dimensions +STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(BANDWIDTH_ELEMS) / $(TRANSP_MODE)))) # Transpose tile width corresponds to bandwidth -STIM_WRITE_D0_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(TRANSP_MODE)))) # Transpose: Input matrix height corresponds to output matrix width +STIM_WRITE_D0_STRIDE ?= $(shell echo $$(($(MATRIX_DIM_M) * $(TRANSP_MODE)))) # Transpose: Input matrix height corresponds to output matrix width STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(STIM_WRITE_D0_STRIDE) / $(BANDWIDTH_ELEMS))))# Transpose: Input matrix height corresponds to output matrix width STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # Transpose tile height corresponds to bandwidth -STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(BANDWIDTH_ELEMS)))) # D2 length is controlled by total length -STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read +STIM_WRITE_D2_LENGTH ?= 0 +STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(STIM_WRITE_D0_STRIDE) * $(STIM_WRITE_D0_LENGTH)))) # D2 length is controlled by total length +STIM_WRITE_D3_LENGTH ?= 0 +STIM_WRITE_D3_STRIDE ?= 0 +STIM_WRITE_D4_STRIDE ?= 0 +STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read !!! TODO(cdurrer): FIX +STIM_WRITE_DIM_ENABLE ?= "4'b0011" else ifeq "$(strip $(DATAMOVER_MODE))" "2" # CIM data layout conversion mode $(info CIM data layout conversion mode $(CIM_MODE) enabled) ifneq ($(filter 0 1, $(strip $(CIM_MODE))), $(strip $(CIM_MODE))) $(error "Invalid CIM_MODE $(CIM_MODE): must be 0 or 1") endif -STIM_READ_BASE_ADDR ?= 0 # Element-addressed +STIM_READ_BASE_ADDR ?= $(READ_BASE_ADDR) # Element-addressed STIM_READ_D0_LENGTH ?= $(shell echo $$(($(CIM_INNER_DIM) / $(BANDWIDTH_ELEMS)))) # [Nof accesses with bandwidth BW per D0-transfer ("row")] STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -STIM_READ_D1_LENGTH ?= $(MATRIX_SIZE_M) -STIM_READ_D1_STRIDE ?= $(MATRIX_SIZE_N) -STIM_READ_D2_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(CIM_INNER_DIM)))) # Redundant (handled by TOT_LEN) +STIM_READ_D1_LENGTH ?= $(MATRIX_DIM_M) +STIM_READ_D1_STRIDE ?= $(MATRIX_DIM_N) +STIM_READ_D2_LENGTH ?= $(shell echo $$(($(MATRIX_DIM_N) / $(CIM_INNER_DIM)))) # Redundant (handled by TOT_LEN) STIM_READ_D2_STRIDE ?= $(CIM_INNER_DIM) +STIM_READ_D3_LENGTH ?= 0 +STIM_READ_D3_STRIDE ?= 0 +STIM_READ_D4_STRIDE ?= 0 PARTIAL_MULT = $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(PARTIAL_MULT) * $(STIM_READ_D2_LENGTH)))) # [Total memory accesses] +STIM_READ_DIM_ENABLE ?= "4'b0011" STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= 0 -STIM_TRANSP_LEN ?= 0 -STIM_MATRIX_SIZE_M ?= $(MATRIX_SIZE_M) -STIM_MATRIX_SIZE_N ?= $(MATRIX_SIZE_N) +# STIM_TRANSP_LEN ?= 0 +STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) +STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed -STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(MATRIX_SIZE_M)))) +STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(MATRIX_DIM_M)))) STIM_WRITE_D0_STRIDE ?= $(BANDWIDTH_ELEMS) -STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_N) / $(CIM_INNER_DIM)))) +STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(MATRIX_DIM_N) / $(CIM_INNER_DIM)))) STIM_WRITE_D1_STRIDE ?= $(shell echo $$(($(STIM_WRITE_D0_LENGTH) * $(BANDWIDTH_ELEMS)))) +STIM_WRITE_D2_LENGTH ?= 0 STIM_WRITE_D2_STRIDE ?= 0 +STIM_WRITE_D3_LENGTH ?= 0 +STIM_WRITE_D3_STRIDE ?= 0 +STIM_WRITE_D4_STRIDE ?= 0 STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read +STIM_WRITE_DIM_ENABLE ?= "4'b0001" else ifeq "$(strip $(DATAMOVER_MODE))" "3" # CIM data layout transpose mode $(info CIM data layout transpose mode $(CIM_MODE) enabled) @@ -138,28 +180,37 @@ ifneq ($(filter 1 2 4,$(strip $(TRANSP_MODE))), $(strip $(TRANSP_MODE))) $(error Invalid TRANSP_MODE $(TRANSP_MODE): must be 1, 2, or 4) endif -STIM_READ_BASE_ADDR ?= 0 # Element-addressed -STIM_READ_D0_LENGTH ?= $(MATRIX_SIZE_M) +STIM_READ_BASE_ADDR ?= $(READ_BASE_ADDR) # Element-addressed +STIM_READ_D0_LENGTH ?= $(MATRIX_DIM_M) STIM_READ_D0_STRIDE ?= $(CIM_INNER_DIM) STIM_READ_D1_LENGTH ?= $(shell echo $$(($(CIM_INNER_DIM) / $(BANDWIDTH_ELEMS)))) STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) STIM_READ_D2_LENGTH ?= 0 # Not used (controlled by TOT_LEN) -STIM_READ_D2_STRIDE ?= $(shell echo $$(($(MATRIX_SIZE_M) * $(CIM_INNER_DIM)))) +STIM_READ_D2_STRIDE ?= $(shell echo $$(($(MATRIX_DIM_M) * $(CIM_INNER_DIM)))) +STIM_READ_D3_LENGTH ?= 0 +STIM_READ_D3_STRIDE ?= 0 +STIM_READ_D4_STRIDE ?= 0 STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS)))) +STIM_READ_DIM_ENABLE ?= "4'b0011" STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= $(TRANSP_MODE) # transp_mode -STIM_TRANSP_LEN ?= 0 -STIM_MATRIX_SIZE_M ?= $(MATRIX_SIZE_M) -STIM_MATRIX_SIZE_N ?= $(MATRIX_SIZE_N) +# STIM_TRANSP_LEN ?= 0 +STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) +STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(BANDWIDTH_ELEMS) STIM_WRITE_D0_STRIDE ?= $(CIM_INNER_DIM) STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(CIM_INNER_DIM) / $(BANDWIDTH_ELEMS)))) STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) +STIM_WRITE_D2_LENGTH ?= 0 STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(BANDWIDTH_ELEMS) * $(CIM_INNER_DIM)))) +STIM_WRITE_D3_LENGTH ?= 0 +STIM_WRITE_D3_STRIDE ?= 0 +STIM_WRITE_D4_STRIDE ?= 0 STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read +STIM_WRITE_DIM_ENABLE ?= "4'b0011" else @@ -183,24 +234,32 @@ $(info CIM_INNER_DIM: $(CIM_INNER_DIM) elements) $(info CIM_OUTER_DIM: $(CIM_OUTER_DIM) elements) $(info ) $(info Matrix Configuration:) -$(info MATRIX_SIZE: $(MATRIX_SIZE_M) x $(MATRIX_SIZE_N)) +$(info MATRIX_SIZE: $(MATRIX_DIM_M) x $(MATRIX_DIM_N)) +$(info MATRIX SIZE ALIGNED: $(MATRIX_DIM_M_ALIGNED) x $(MATRIX_DIM_N_ALIGNED)) $(info MEMORY_SIZE: $(MEMORY_SIZE) words) +$(info STIM_MEM_SIZE: $(STIM_MEM_SIZE) words) $(info ) $(info Read Configuration:) $(info BASE_ADDR: $(STIM_READ_BASE_ADDR)) $(info D0_LENGTH: $(STIM_READ_D0_LENGTH), D0_STRIDE: $(STIM_READ_D0_STRIDE)) $(info D1_LENGTH: $(STIM_READ_D1_LENGTH), D1_STRIDE: $(STIM_READ_D1_STRIDE)) -$(info D2_STRIDE: $(STIM_READ_D2_STRIDE)) +$(info D2_LENGTH: $(STIM_READ_D2_LENGTH), D2_STRIDE: $(STIM_READ_D2_STRIDE)) +$(info D3_LENGTH: $(STIM_READ_D3_LENGTH), D3_STRIDE: $(STIM_READ_D3_STRIDE)) +$(info D4_STRIDE: $(STIM_READ_D4_STRIDE)) $(info TOT_LENGTH: $(STIM_READ_TOT_LENGTH)) +$(info DIM_ENABLE: $(STIM_READ_DIM_ENABLE)) $(info ) $(info Write Configuration:) $(info BASE_ADDR: $(STIM_WRITE_BASE_ADDR)) $(info D0_LENGTH: $(STIM_WRITE_D0_LENGTH), D0_STRIDE: $(STIM_WRITE_D0_STRIDE)) $(info D1_LENGTH: $(STIM_WRITE_D1_LENGTH), D1_STRIDE: $(STIM_WRITE_D1_STRIDE)) -$(info D2_STRIDE: $(STIM_WRITE_D2_STRIDE)) +$(info D2_LENGTH: $(STIM_WRITE_D2_LENGTH), D2_STRIDE: $(STIM_WRITE_D2_STRIDE)) +$(info D3_LENGTH: $(STIM_WRITE_D3_LENGTH), D3_STRIDE: $(STIM_WRITE_D3_STRIDE)) +$(info D4_STRIDE: $(STIM_WRITE_D4_STRIDE)) $(info TOT_LENGTH: $(STIM_WRITE_TOT_LENGTH)) +$(info DIM_ENABLE: $(STIM_WRITE_DIM_ENABLE)) $(info ) $(info Transpose Configuration:) $(info TRANSP_MODE: $(STIM_TRANSP_MODE)) -$(info TRANSP_LEN: $(STIM_TRANSP_LEN)) +# $(info TRANSP_LEN: $(STIM_TRANSP_LEN)) $(info ========================================) diff --git a/config_presets.mk b/config_presets.mk index da7b270..d8ffe38 100644 --- a/config_presets.mk +++ b/config_presets.mk @@ -40,8 +40,8 @@ ifeq ($(CONFIG_PRESET),small-matrix) CIM_MODE = 0 CIM_INNER_DIM = 0 CIM_OUTER_DIM = 0 - MATRIX_SIZE_M = 4 - MATRIX_SIZE_N = 4 + MATRIX_DIM_M = 4 + MATRIX_DIM_N = 4 CONFIG_DESC = "Small 4x4 matrix, 1-element transpose" endif @@ -56,8 +56,8 @@ ifeq ($(CONFIG_PRESET),medium-matrix) CIM_MODE = 0 CIM_INNER_DIM = 0 CIM_OUTER_DIM = 0 - MATRIX_SIZE_M = 64 - MATRIX_SIZE_N = 64 + MATRIX_DIM_M = 64 + MATRIX_DIM_N = 64 CONFIG_DESC = "Medium 64x64 matrix, 1-element transpose" endif @@ -72,8 +72,8 @@ ifeq ($(CONFIG_PRESET),large-matrix) CIM_MODE = 0 CIM_INNER_DIM = 0 CIM_OUTER_DIM = 0 - MATRIX_SIZE_M = 448 - MATRIX_SIZE_N = 448 + MATRIX_DIM_M = 448 + MATRIX_DIM_N = 448 CONFIG_DESC = "Large 448x448 matrix, 1-element transpose" endif @@ -88,8 +88,8 @@ ifeq ($(CONFIG_PRESET),transpose-test) CIM_MODE = 0 CIM_INNER_DIM = 0 CIM_OUTER_DIM = 0 - MATRIX_SIZE_M = 32 - MATRIX_SIZE_N = 32 + MATRIX_DIM_M = 32 + MATRIX_DIM_N = 32 CONFIG_DESC = "32x32 matrix, 2-element transpose" endif @@ -104,8 +104,8 @@ ifeq ($(CONFIG_PRESET),rect-wide) CIM_MODE = 0 CIM_INNER_DIM = 0 CIM_OUTER_DIM = 0 - MATRIX_SIZE_M = 64 - MATRIX_SIZE_N = 256 + MATRIX_DIM_M = 64 + MATRIX_DIM_N = 256 CONFIG_DESC = "Wide rectangular matrix 64x256, 4-element transpose" endif @@ -120,8 +120,8 @@ ifeq ($(CONFIG_PRESET),rect-tall) CIM_MODE = 0 CIM_INNER_DIM = 0 CIM_OUTER_DIM = 0 - MATRIX_SIZE_M = 256 - MATRIX_SIZE_N = 64 + MATRIX_DIM_M = 256 + MATRIX_DIM_N = 64 CONFIG_DESC = "Tall rectangular matrix 256x64, 2-element transpose" endif @@ -136,8 +136,8 @@ ifeq ($(CONFIG_PRESET),rect-narrow) CIM_MODE = 0 CIM_INNER_DIM = 0 CIM_OUTER_DIM = 0 - MATRIX_SIZE_M = 16 - MATRIX_SIZE_N = 128 + MATRIX_DIM_M = 16 + MATRIX_DIM_N = 128 CONFIG_DESC = "Narrow rectangular matrix 16x128, 1-element transpose" endif @@ -152,8 +152,8 @@ ifeq ($(CONFIG_PRESET),rect-elongated) CIM_MODE = 0 CIM_INNER_DIM = 0 CIM_OUTER_DIM = 0 - MATRIX_SIZE_M = 128 - MATRIX_SIZE_N = 32 + MATRIX_DIM_M = 128 + MATRIX_DIM_N = 32 CONFIG_DESC = "Elongated rectangular matrix 128x32, 2-element transpose" endif @@ -168,8 +168,8 @@ ifeq ($(CONFIG_PRESET),copy-small) CIM_MODE = 0 CIM_INNER_DIM = 0 CIM_OUTER_DIM = 0 - MATRIX_SIZE_M = 4 - MATRIX_SIZE_N = 4 + MATRIX_DIM_M = 4 + MATRIX_DIM_N = 4 CONFIG_DESC = "Small 4x4 matrix, copy mode" endif @@ -184,8 +184,8 @@ ifeq ($(CONFIG_PRESET),copy-medium) CIM_MODE = 0 CIM_INNER_DIM = 0 CIM_OUTER_DIM = 0 - MATRIX_SIZE_M = 64 - MATRIX_SIZE_N = 64 + MATRIX_DIM_M = 64 + MATRIX_DIM_N = 64 CONFIG_DESC = "Medium 64x64 matrix, copy mode" endif @@ -200,8 +200,8 @@ ifeq ($(CONFIG_PRESET),cim-small) CIM_MODE = 0 CIM_INNER_DIM = 32 CIM_OUTER_DIM = 16 - MATRIX_SIZE_M = 32 - MATRIX_SIZE_N = 128 + MATRIX_DIM_M = 32 + MATRIX_DIM_N = 128 CONFIG_DESC = "CIM 32x128 matrix, CIM_INNER_DIM=32, 128-bit bandwidth" endif @@ -216,8 +216,8 @@ ifeq ($(CONFIG_PRESET),cim-medium) CIM_MODE = 0 CIM_INNER_DIM = 64 CIM_OUTER_DIM = 32 - MATRIX_SIZE_M = 64 - MATRIX_SIZE_N = 256 + MATRIX_DIM_M = 64 + MATRIX_DIM_N = 256 CONFIG_DESC = "CIM 64x256 matrix, CIM_INNER_DIM=64, 256-bit bandwidth" endif @@ -232,8 +232,8 @@ ifeq ($(CONFIG_PRESET),cim-large) CIM_MODE = 0 CIM_INNER_DIM = 64 CIM_OUTER_DIM = 64 - MATRIX_SIZE_M = 128 - MATRIX_SIZE_N = 256 + MATRIX_DIM_M = 128 + MATRIX_DIM_N = 256 CONFIG_DESC = "CIM 128x256 matrix, CIM_INNER_DIM=64, 512-bit bandwidth" endif diff --git a/rtl/datamover_engine.sv b/rtl/datamover_engine.sv index 543c448..e165e1d 100644 --- a/rtl/datamover_engine.sv +++ b/rtl/datamover_engine.sv @@ -35,10 +35,8 @@ module datamover_engine // local enable & clear input logic enable_i, input logic clear_i, - // FIXME make it ctrl + // control registers input ctrl_engine_t ctrl_i, - // input logic [31:0] matrix_size_m_i, - // input logic [31:0] matrix_size_n_i, // input data stream + handshake hwpe_stream_intf_stream.sink data_in, // output data stream + handshake @@ -48,7 +46,7 @@ module datamover_engine // number of elements (in the full bandwidth, not a single bank word) localparam NB_ELEMENTS = BANDWIDTH_ALIGNED / ELEM_WIDTH; logic [23:0] matrix_tot_size; - assign matrix_tot_size = ctrl_i.matrix_size_m * ctrl_i.matrix_size_n; // ToDo(cdurrer): additional MUL, problem? + assign matrix_tot_size = ctrl_i.matrix_dim_m * ctrl_i.matrix_dim_n; // ToDo(cdurrer): additional MUL, problem? logic [$clog2(NB_ELEMENTS)-1:0] remaining_elems; assign remaining_elems = matrix_tot_size % NB_ELEMENTS; logic [11:0] nof_accesses; diff --git a/rtl/datamover_package.sv b/rtl/datamover_package.sv index 9941304..4ced8c4 100644 --- a/rtl/datamover_package.sv +++ b/rtl/datamover_package.sv @@ -31,13 +31,13 @@ package datamover_package; parameter int unsigned MAX_BANDWIDTH = 512; // support maximum 512bits of bandwidth - typedef enum logic[1:0] { TRANSP_4ELEM, TRANSP_2ELEM, TRANSP_1ELEM, TRANSP_NONE } transp_mode_e; + typedef enum logic[1:0] { TRANSP_NONE, TRANSP_1ELEM, TRANSP_2ELEM, TRANSP_4ELEM } transp_mode_e; typedef struct packed { transp_mode_e transp_mode; logic [$clog2(MAX_BANDWIDTH/8):0] transp_len; logic [2:0] transp_stride; // 1, 2, or 4 - logic [11:0] matrix_size_m; - logic [11:0] matrix_size_n; + logic [11:0] matrix_dim_m; + logic [11:0] matrix_dim_n; } ctrl_engine_t; parameter int unsigned HWPE_REGISTER_OFFS = 32'h00; // Standard HWPE register offset @@ -56,20 +56,21 @@ package datamover_package; parameter int unsigned DATAMOVER_REGISTER_CXT0_OFFS = 32'h80; // Context 0 offset parameter int unsigned DATAMOVER_REGISTER_CXT1_OFFS = 32'h120; // Context 1 offset - // Job-specific registers - parameter int unsigned DATAMOVER_REG_IN_PTR = 32'h00; // Input pointer - parameter int unsigned DATAMOVER_REG_OUT_PTR = 32'h04; // Output pointer - parameter int unsigned DATAMOVER_REG_LEN0 = 32'h08; // [31:24] in_d1_len[7:0]; [23:12] in_d0_len; [11:0] tot_len - parameter int unsigned DATAMOVER_REG_LEN1 = 32'h0C; // [27:24] in_d1_len[11:8]; [23:12] out_d1_len; [11:0] out_d0_len - parameter int unsigned DATAMOVER_REG_IN_D0_STRIDE = 32'h10; // Input dimension 0 stride - parameter int unsigned DATAMOVER_REG_IN_D1_STRIDE = 32'h14; // Input dimension 1 stride - parameter int unsigned DATAMOVER_REG_IN_D2_STRIDE = 32'h18; // Input dimension 2 stride - parameter int unsigned DATAMOVER_REG_OUT_D0_STRIDE = 32'h1C; // Output dimension 0 stride - parameter int unsigned DATAMOVER_REG_OUT_D1_STRIDE = 32'h20; // Output dimension 1 stride - parameter int unsigned DATAMOVER_REG_OUT_D2_STRIDE = 32'h24; // Output dimension 2 stride - parameter int unsigned DATAMOVER_REG_TRANSP_MODE = 32'h28; // Transposition mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) - // Leftover: [31:16], if 0 then no leftover - parameter int unsigned DATAMOVER_REG_MATRIX_SIZE = 32'h2C; // [31:24] unused; [23:12] matrix_size_n; [11:0] matrix_size_m + // Job-specific registers (packed into 32-bit words to speed up configuration and save memory) + parameter int unsigned DATAMOVER_REG_IN_PTR = 32'h00; // Input pointer + parameter int unsigned DATAMOVER_REG_OUT_PTR = 32'h04; // Output pointer + parameter int unsigned DATAMOVER_REG_TOT_LEN = 32'h08; // Total length in number of accesses (BW) + parameter int unsigned DATAMOVER_REG_IN_D0 = 32'h0C; // [31:16] in_d0_stride; [15:0] in_d0_len + parameter int unsigned DATAMOVER_REG_IN_D1 = 32'h10; // [31:16] in_d1_stride; [15:0] in_d1_len + parameter int unsigned DATAMOVER_REG_IN_D2 = 32'h14; // [31:16] in_d2_stride; [15:0] in_d2_len + parameter int unsigned DATAMOVER_REG_IN_D3 = 32'h18; // [31:16] in_d3_stride; [15:0] in_d3_len + parameter int unsigned DATAMOVER_REG_OUT_D0 = 32'h1C; // [31:16] out_d0_stride; [15:0] out_d0_len + parameter int unsigned DATAMOVER_REG_OUT_D1 = 32'h20; // [31:16] out_d1_stride; [15:0] out_d1_len + parameter int unsigned DATAMOVER_REG_OUT_D2 = 32'h24; // [31:16] out_d2_stride; [15:0] out_d2_len + parameter int unsigned DATAMOVER_REG_OUT_D3 = 32'h28; // [31:16] out_d3_stride; [15:0] out_d3_len + parameter int unsigned DATAMOVER_REG_IN_OUT_D4_STRIDE = 32'h2C; // [31:16] out_d4_stride; [15:0] in_d4_stride (d4_len unnecessary due to tot_len) + parameter int unsigned DATAMOVER_REG_DIM_ENABLE = 32'h30; // [31:8] unused;[7:4] write_dim_en; [3:0] read_dim_en -> one-hot encoding (LSB->d1), d0 is always enabled + parameter int unsigned DATAMOVER_REG_CTRL_ENGINE = 32'h34; // [31:27] unused; [26:15] matrix_dim_n; [14:3] matrix_dim_m [2:0] transp_mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) // Note: increase N_IO_REGS in datamover_top.sv when adding new registers here! diff --git a/rtl/datamover_top.sv b/rtl/datamover_top.sv index 9166074..016a19e 100644 --- a/rtl/datamover_top.sv +++ b/rtl/datamover_top.sv @@ -148,7 +148,7 @@ module datamover_top .REGFILE_SCM ( 0 ), .N_CORES ( N_CORES ), .N_CONTEXT ( N_CONTEXT ), - .N_IO_REGS ( 12 ), + .N_IO_REGS ( 14 ), .N_GENERIC_REGS ( 8 ), .ID_WIDTH ( ID ) ) i_slave ( @@ -213,42 +213,51 @@ module datamover_top always_comb begin streamer_ctrl_cfg = '0; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.dim_enable_1h = 4'b0011; // Reading operation needs 3 dimensions (activating d0 and d1, d2 is controlled by tot_len) - streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.dim_enable_1h = 4'b0011; // Writing operation needs 3 dimensions (activating d0 and d1, d2 is controlled by tot_len) + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.dim_enable_1h = reg_file.hwpe_params[DATAMOVER_REG_DIM_ENABLE >> 2][3:0]; // Enabled dimensions (d0 is always enabled) + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.dim_enable_1h = reg_file.hwpe_params[DATAMOVER_REG_DIM_ENABLE >> 2][7:4]; // Enabled dimensions (d0 is always enabled) streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.base_addr = reg_file.hwpe_params[DATAMOVER_REG_IN_PTR >> 2]; streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.base_addr = reg_file.hwpe_params[DATAMOVER_REG_OUT_PTR >> 2]; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.tot_len = reg_file.hwpe_params[DATAMOVER_REG_LEN0 >> 2][11:0]; - streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.tot_len = reg_file.hwpe_params[DATAMOVER_REG_LEN0 >> 2][11:0]; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d0_len = reg_file.hwpe_params[DATAMOVER_REG_LEN0 >> 2][23:12]; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d0_stride = reg_file.hwpe_params[DATAMOVER_REG_IN_D0_STRIDE >> 2]; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d1_len = { reg_file.hwpe_params[DATAMOVER_REG_LEN1 >> 2][27:24], reg_file.hwpe_params[DATAMOVER_REG_LEN0 >> 2][31:24] }; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d1_stride = reg_file.hwpe_params[DATAMOVER_REG_IN_D1_STRIDE >> 2]; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d2_stride = reg_file.hwpe_params[DATAMOVER_REG_IN_D2_STRIDE >> 2]; - streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d0_len = reg_file.hwpe_params[DATAMOVER_REG_LEN1 >> 2][11:0]; - streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d0_stride = reg_file.hwpe_params[DATAMOVER_REG_OUT_D0_STRIDE >> 2]; - streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d1_len = reg_file.hwpe_params[DATAMOVER_REG_LEN1 >> 2][23:12]; - streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d1_stride = reg_file.hwpe_params[DATAMOVER_REG_OUT_D1_STRIDE >> 2]; - streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d2_stride = reg_file.hwpe_params[DATAMOVER_REG_OUT_D2_STRIDE >> 2]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.tot_len = reg_file.hwpe_params[DATAMOVER_REG_TOT_LEN >> 2][11:0]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.tot_len = reg_file.hwpe_params[DATAMOVER_REG_TOT_LEN >> 2][11:0]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d0_len = reg_file.hwpe_params[DATAMOVER_REG_IN_D0 >> 2][15:0]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d0_stride = reg_file.hwpe_params[DATAMOVER_REG_IN_D0 >> 2][31:16]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d1_len = reg_file.hwpe_params[DATAMOVER_REG_IN_D1 >> 2][15:0]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d1_stride = reg_file.hwpe_params[DATAMOVER_REG_IN_D1 >> 2][31:16]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d2_len = reg_file.hwpe_params[DATAMOVER_REG_IN_D2 >> 2][15:0]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d2_stride = reg_file.hwpe_params[DATAMOVER_REG_IN_D2 >> 2][31:16]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d3_len = reg_file.hwpe_params[DATAMOVER_REG_IN_D3 >> 2][15:0]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d3_stride = reg_file.hwpe_params[DATAMOVER_REG_IN_D3 >> 2][31:16]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d4_stride = reg_file.hwpe_params[DATAMOVER_REG_IN_OUT_D4_STRIDE >> 2][15:0]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d0_len = reg_file.hwpe_params[DATAMOVER_REG_OUT_D0 >> 2][15:0]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d0_stride = reg_file.hwpe_params[DATAMOVER_REG_OUT_D0 >> 2][31:16]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d1_len = reg_file.hwpe_params[DATAMOVER_REG_OUT_D1 >> 2][15:0]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d1_stride = reg_file.hwpe_params[DATAMOVER_REG_OUT_D1 >> 2][31:16]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d2_len = reg_file.hwpe_params[DATAMOVER_REG_OUT_D2 >> 2][15:0]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d2_stride = reg_file.hwpe_params[DATAMOVER_REG_OUT_D2 >> 2][31:16]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d3_len = reg_file.hwpe_params[DATAMOVER_REG_OUT_D3 >> 2][15:0]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d3_stride = reg_file.hwpe_params[DATAMOVER_REG_OUT_D3 >> 2][31:16]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.d4_stride = reg_file.hwpe_params[DATAMOVER_REG_IN_OUT_D4_STRIDE >> 2][31:16]; end // Binding of engine configuration always_comb begin engine_ctrl = '0; - engine_ctrl.transp_mode = reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b000 ? TRANSP_NONE : - reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b001 ? TRANSP_1ELEM : - reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b010 ? TRANSP_2ELEM : TRANSP_4ELEM; - engine_ctrl.transp_stride = reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b000 ? 1 : - reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b001 ? 1 : - reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][2:0] == 3'b010 ? 2 : 4; - engine_ctrl.matrix_size_m = reg_file.hwpe_params[DATAMOVER_REG_MATRIX_SIZE >> 2][11:0]; - engine_ctrl.matrix_size_n = reg_file.hwpe_params[DATAMOVER_REG_MATRIX_SIZE >> 2][23:12]; - if(reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16] == '0) begin // no leftover - engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; // ToDo(cdurrer): why? - end - else begin // in case of leftover, use the reg content as length - engine_ctrl.transp_len = reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16]; // ToDo(cdurrer): mention this option in config/pkg/TB - end + engine_ctrl.transp_mode = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b000 ? TRANSP_NONE : + reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b001 ? TRANSP_1ELEM : + reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b010 ? TRANSP_2ELEM : TRANSP_4ELEM; + engine_ctrl.transp_stride = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b000 ? 1 : + reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b001 ? 1 : + reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b010 ? 2 : 4; + engine_ctrl.matrix_dim_m = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][14:3]; + engine_ctrl.matrix_dim_n = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][26:15]; + engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; // ToDo(cdurrer): What exactly did transp_len do? Use it to handle leftovers? + // if(reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16] == '0) begin // no leftover + // engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; // ToDo(cdurrer): why? + // end + // else begin // in case of leftover, use the reg content as length + // engine_ctrl.transp_len = reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16]; // ToDo(cdurrer): mention this option in config/pkg/TB + // end end // Bind the output event, which is propagated to the event unit and used diff --git a/test/hal_datamover.h b/test/hal_datamover.h index c1cc53e..bf960e7 100644 --- a/test/hal_datamover.h +++ b/test/hal_datamover.h @@ -41,7 +41,7 @@ #define DATAMOVER_SWSYNC 0x18 #define DATAMOVER_URISCY_IMEM 0x1c -// job configuration ToDo(cdurrer): DIFFERENT TO datamover_package.sv! Why? +// job configuration ToDo(cdurrer): OUTDATED! Use HAL from Konark-cluster repo! #define DATAMOVER_REGISTER_OFFS 0x40 #define DATAMOVER_REGISTER_CXT0_OFFS 0x80 #define DATAMOVER_REGISTER_CXT1_OFFS 0x120 diff --git a/verif/python/datamover_golden_model_numpy.py b/verif/python/datamover_golden_model_numpy.py new file mode 100644 index 0000000..fe3a26b --- /dev/null +++ b/verif/python/datamover_golden_model_numpy.py @@ -0,0 +1,61 @@ +import random +import argparse +import os +import math +from unittest import case +import numpy as np + +RANDOM_STIMULI = False # If False, stimuli are generated in a counting fashion + +CIM_MODE = 4 # 0: copy, 1: transpose, 2: CIM data layout conversion, 3: CIM layout transpose, 4: unfold + +CHANNELS = 2 +HEIGHT = 4 +WIDTH = 4 +PATCH_SIZE = 4 + +def unfold(tensor, patch_size): + # Input tensor shape: (CHANNELS, HEIGHT, WIDTH) + # Output tensor shape: (PATCH_SIZE, NUM_PATCHES, CHANNELS) + channels, height, width = tensor.shape + patch_sidelength = int(math.sqrt(patch_size)) + assert (height % patch_sidelength == 0) and (width % patch_sidelength == 0), "Height and Width must be divisible by patch sidelength" + num_patches_h = height // patch_sidelength + num_patches_w = width // patch_sidelength + num_patches = num_patches_h * num_patches_w + tensor_unfolded = np.zeros((patch_size, num_patches, channels), dtype=tensor.dtype) + for p in range(patch_size): + for h in range(num_patches_h): + for w in range(num_patches_w): + n = h * num_patches_w + w + h_idx = h * patch_sidelength + (p // patch_sidelength) + w_idx = w * patch_sidelength + (p % patch_sidelength) + tensor_unfolded[p, n, :] = tensor[:, h_idx, w_idx] + return tensor_unfolded + +def main(): + # Create a tensor of size (CHANNELS, HEIGHT, WIDTH) with random or counting values + if RANDOM_STIMULI: + input_tensor = np.random.randint(0, 256, (CHANNELS, HEIGHT, WIDTH), dtype=np.uint8) + else: + input_tensor = np.arange(CHANNELS * HEIGHT * WIDTH, dtype=np.uint8).reshape((CHANNELS, HEIGHT, WIDTH)) + print("Input Tensor:") + print(input_tensor) + if CIM_MODE==0: + output_tensor = input_tensor.copy() + elif CIM_MODE==1: + output_tensor = np.transpose(input_tensor, (0, 2, 1)) + # elif CIM_MODE==2: + # output_tensor = cim_layout(input_tensor, ) + # elif CIM_MODE==3: + # output_tensor = cim_transpose(input_tensor, ) + elif CIM_MODE==4: + output_tensor = unfold(input_tensor, PATCH_SIZE) + else: + raise ValueError(f"Unsupported CIM_MODE: {CIM_MODE}") + + print("\nOutput Tensor:") + print(output_tensor) + +if __name__ == "__main__": + main() diff --git a/verif/python/datamover_microarchitectural_model.py b/verif/python/datamover_microarchitectural_model.py new file mode 100644 index 0000000..2721fb2 --- /dev/null +++ b/verif/python/datamover_microarchitectural_model.py @@ -0,0 +1,299 @@ +import random +import argparse +import os +import math +from unittest import case +import numpy as np +from dataclasses import dataclass + +RANDOM_STIMULI = False # If False, stimuli are generated in a counting fashion + +# HW Parameters +BANDWIDTH = 32 # in bits +WORD_WIDTH = 32 # in bits +ELEM_WIDTH = 8 # in bits +BANDWIDTH_ELEMS = BANDWIDTH // ELEM_WIDTH +WORDWIDTH_ELEMS = WORD_WIDTH // ELEM_WIDTH + +# Operation Modes +DATAMOVER_MODE = 4 # 0: copy, 1: transpose, 2: CIM data layout conversion, 3: CIM layout transpose, 4: unfold +TRANSP_MODE = 1 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted +CIM_ROWTILE_SIZE = 2 # in elements (64 in Konark) +PATCH_SIZE = 4 # in elements (2x2 = 4 in MobileViT) + +CHANNELS = 2 +HEIGHT = 8 +WIDTH = 8 + +@dataclass +class addrgen_ctrl_c: + D0_LENGTH: int = HEIGHT * WIDTH + D0_STRIDE: int = BANDWIDTH_ELEMS + D1_LENGTH: int = 0 + D1_STRIDE: int = 0 + D2_LENGTH: int = 0 + D2_STRIDE: int = 0 + D3_LENGTH: int = 0 + D3_STRIDE: int = 0 + D4_STRIDE: int = 0 + TOT_LENGTH: int = math.ceil(HEIGHT * WIDTH / BANDWIDTH_ELEMS) + # DIM_ENABLE: str = "0000" # !!! REVERSE ORDER than in SystemVerilog testbench / config.mk + +class addrgen_c: + def __init__(self, start, addrgen_ctrl): + self.addr = start + self.addrgen_ctrl = addrgen_ctrl + + # loop indices (counters) + self.d0 = 0 + self.d1 = 0 + self.d2 = 0 + self.d3 = 0 + self.d4 = 0 + + def tick(self): + out = int(self.addr) + # ---- D0 ---- + self.d0 += 1 + self.addr += self.addrgen_ctrl.D0_STRIDE + if self.d0 < self.addrgen_ctrl.D0_LENGTH: + return out + # wrap D0 + self.d0 = 0 + self.addr -= self.addrgen_ctrl.D0_LENGTH * self.addrgen_ctrl.D0_STRIDE + # ---- D1 ---- + self.d1 += 1 + self.addr += self.addrgen_ctrl.D1_STRIDE + if self.d1 < self.addrgen_ctrl.D1_LENGTH: + return out + self.d1 = 0 + self.addr -= self.addrgen_ctrl.D1_LENGTH * self.addrgen_ctrl.D1_STRIDE + # ---- D2 ---- + self.d2 += 1 + self.addr += self.addrgen_ctrl.D2_STRIDE + if self.d2 < self.addrgen_ctrl.D2_LENGTH: + return out + self.d2 = 0 + self.addr -= self.addrgen_ctrl.D2_LENGTH * self.addrgen_ctrl.D2_STRIDE + # ---- D3 ---- + self.d3 += 1 + self.addr += self.addrgen_ctrl.D3_STRIDE + if self.d3 < self.addrgen_ctrl.D3_LENGTH: + return out + self.d3 = 0 + self.addr -= self.addrgen_ctrl.D3_LENGTH * self.addrgen_ctrl.D3_STRIDE + # ---- D4 ---- + self.d4 += 1 + self.addr += self.addrgen_ctrl.D4_STRIDE + # if self.d4 < self.addrgen_ctrl.D4_LENGTH: + # return self.addr + # final wrap (optional) + # self.d4 = 0 + # self.addr = self.start + return out + +def datamover_execute(input_memory, input_channels, input_height, input_width, addrgen_in_ctrl, addrgen_out_ctrl): + nof_input_tiles = math.ceil(addrgen_in_ctrl.TOT_LENGTH / BANDWIDTH_ELEMS) + elem_matrix = np.zeros((BANDWIDTH_ELEMS, BANDWIDTH_ELEMS), dtype=input_memory.dtype) + output_memory = np.zeros_like(input_memory) + addrgen_in = addrgen_c(0, addrgen_in_ctrl) + addrgen_out = addrgen_c(0, addrgen_out_ctrl) + + for input_tile_idx in range(nof_input_tiles): + # Fill the element matrix (internal buffer) + for i in range(BANDWIDTH_ELEMS): + in_addr = addrgen_in.tick() + # print(f"Reading input address: {in_addr}") + elem_matrix[i, :] = input_memory[in_addr:in_addr + BANDWIDTH_ELEMS] + print(f"elem_matrix (tile {input_tile_idx}):\n{elem_matrix}\n") + # Write out the element matrix to output memory (rearranged as needed) + for i in range(BANDWIDTH_ELEMS): + out_addr = addrgen_out.tick() + if DATAMOVER_MODE==0 or DATAMOVER_MODE==2: + output_memory[out_addr:out_addr + BANDWIDTH_ELEMS] = elem_matrix[i, :] + elif DATAMOVER_MODE==1: + if TRANSP_MODE==1: + output_memory[out_addr:out_addr + BANDWIDTH_ELEMS] = elem_matrix[:, i] + else: + print(f"Unsupported TRANSP_MODE {TRANSP_MODE} in datamover_execute") + elif DATAMOVER_MODE==4: # ToDo(cdurrer) + for x in range(BANDWIDTH_ELEMS): + for y in range(BANDWIDTH_ELEMS): + output_memory[out_addr + x * BANDWIDTH_ELEMS + y] = elem_matrix[x, y] + else: + print(f"Unsupported DATAMOVER_MODE {DATAMOVER_MODE} in datamover_execute") + return output_memory + +def cim_copy_config(input_channels, input_height, input_width): + total_elems = input_channels * input_height * input_width + total_accesses = math.ceil(total_elems / BANDWIDTH_ELEMS) + addrgen_in_ctrl = addrgen_ctrl_c( + D0_LENGTH=total_accesses, + D0_STRIDE=BANDWIDTH_ELEMS, + D1_LENGTH=0, + D1_STRIDE=0, + D2_LENGTH=0, + D2_STRIDE=0, + D3_LENGTH=0, + D3_STRIDE=0, + D4_STRIDE=0, + TOT_LENGTH=total_accesses, + # DIM_ENABLE="0000", + ) + addrgen_out_ctrl = addrgen_ctrl_c( + D0_LENGTH=total_accesses, + D0_STRIDE=BANDWIDTH_ELEMS, + D1_LENGTH=0, + D1_STRIDE=0, + D2_LENGTH=0, + D2_STRIDE=0, + D3_LENGTH=0, + D3_STRIDE=0, + D4_STRIDE=0, + TOT_LENGTH=total_accesses, + # DIM_ENABLE="0000", + ) + return addrgen_in_ctrl, addrgen_out_ctrl + +def transpose_config(input_height, input_width, transp_mode): + m_aligned = ((input_height + BANDWIDTH_ELEMS - 1) // BANDWIDTH_ELEMS) * BANDWIDTH_ELEMS + n_aligned = ((input_width + BANDWIDTH_ELEMS - 1) // BANDWIDTH_ELEMS) * BANDWIDTH_ELEMS + addrgen_in_ctrl = addrgen_ctrl_c( + D0_LENGTH=m_aligned, + D0_STRIDE=input_width, + D1_LENGTH=n_aligned // BANDWIDTH_ELEMS, + D1_STRIDE=BANDWIDTH_ELEMS, + D2_LENGTH=0, + D2_STRIDE=0, + D3_LENGTH=0, + D3_STRIDE=0, + D4_STRIDE=0, + TOT_LENGTH=m_aligned * (n_aligned // BANDWIDTH_ELEMS), + # DIM_ENABLE="0001", + ) + write_d0_stride = input_height * transp_mode + addrgen_out_ctrl = addrgen_ctrl_c( + D0_LENGTH=BANDWIDTH_ELEMS // transp_mode, + D0_STRIDE=write_d0_stride, + D1_LENGTH=write_d0_stride // BANDWIDTH_ELEMS, + D1_STRIDE=BANDWIDTH_ELEMS, + D2_LENGTH=0, + D2_STRIDE=write_d0_stride * (BANDWIDTH_ELEMS // transp_mode), + D3_LENGTH=0, + D3_STRIDE=0, + D4_STRIDE=0, + TOT_LENGTH=m_aligned * (n_aligned // BANDWIDTH_ELEMS), + # DIM_ENABLE="0011", + ) + return addrgen_in_ctrl, addrgen_out_ctrl + +def cim_layout_config(input_height, input_width, rowtile_size): + addrgen_in_ctrl = addrgen_ctrl_c( + D0_LENGTH=rowtile_size // BANDWIDTH_ELEMS, + D0_STRIDE=BANDWIDTH_ELEMS, + D1_LENGTH=input_height, + D1_STRIDE=input_width, + D2_LENGTH=input_width // rowtile_size, + D2_STRIDE=rowtile_size, + D3_LENGTH=0, + D3_STRIDE=0, + D4_STRIDE=0, + TOT_LENGTH=(rowtile_size // BANDWIDTH_ELEMS) * input_height * (input_width // rowtile_size), + # DIM_ENABLE="0011", + ) + addrgen_out_ctrl = addrgen_ctrl_c( + D0_LENGTH=(rowtile_size // BANDWIDTH_ELEMS) * input_height, + D0_STRIDE=BANDWIDTH_ELEMS, + D1_LENGTH=input_width // rowtile_size, + D1_STRIDE=((rowtile_size // BANDWIDTH_ELEMS) * input_height) * BANDWIDTH_ELEMS, + D2_LENGTH=0, + D2_STRIDE=0, + D3_LENGTH=0, + D3_STRIDE=0, + D4_STRIDE=0, + TOT_LENGTH=(rowtile_size // BANDWIDTH_ELEMS) * input_height * (input_width // rowtile_size), + # DIM_ENABLE="0001", + ) + return addrgen_in_ctrl, addrgen_out_ctrl + +def unfold_config(input_channels, input_height, input_width, patch_size): # TODO(cdurrer): verify correctness + patch_sidelength = int(math.sqrt(patch_size)) + num_patches_h = input_height // patch_sidelength + num_patches_w = input_width // patch_sidelength + num_patches = num_patches_h * num_patches_w + total_elems = input_channels * input_height * input_width + total_accesses = math.ceil(total_elems / BANDWIDTH_ELEMS) + addrgen_in_ctrl = addrgen_ctrl_c( + D0_LENGTH=CHANNELS, + D0_STRIDE=HEIGHT * WIDTH, + D1_LENGTH=HEIGHT, + D1_STRIDE=WIDTH, + D2_LENGTH=math.ceil(WIDTH / BANDWIDTH_ELEMS), + D2_STRIDE=BANDWIDTH_ELEMS, + D3_LENGTH=0, + D3_STRIDE=0, + D4_STRIDE=0, + TOT_LENGTH=total_accesses, + # DIM_ENABLE="0000", + ) + addrgen_out_ctrl = addrgen_ctrl_c( + D0_LENGTH=patch_size, + D0_STRIDE=input_channels, + D1_LENGTH=num_patches, + D1_STRIDE=patch_size * input_channels, + D2_LENGTH=0, + D2_STRIDE=0, + D3_LENGTH=0, + D3_STRIDE=0, + D4_STRIDE=0, + TOT_LENGTH=total_accesses, + # DIM_ENABLE="0011", + ) + return addrgen_in_ctrl, addrgen_out_ctrl + + +def main(): + # Create a tensor of size (CHANNELS, HEIGHT, WIDTH) with random or counting values + if RANDOM_STIMULI: + input_tensor = np.random.randint(0, 256, (CHANNELS, HEIGHT, WIDTH), dtype=np.uint8) + else: + input_tensor = np.arange(CHANNELS * HEIGHT * WIDTH, dtype=np.uint8).reshape(CHANNELS, HEIGHT, WIDTH) + print(f"Input Tensor:\n{input_tensor}\n") + input_memory = input_tensor.flatten() + output_memory = np.zeros_like(input_memory) + print(f"Input Memory (flat):\n{input_memory}\n") + if DATAMOVER_MODE==0: + print("\nDATAMOVER_MODE 0: Copy\n") + (addrgen_in_ctrl, addrgen_out_ctrl) = cim_copy_config(CHANNELS, HEIGHT, WIDTH) + output_memory = datamover_execute(input_memory, CHANNELS, HEIGHT, WIDTH, addrgen_in_ctrl, addrgen_out_ctrl) + output_matrix = output_memory.reshape(CHANNELS, HEIGHT, WIDTH) + elif DATAMOVER_MODE==1: + print(f"\nDATAMOVER_MODE 1: Transpose\n") + (addrgen_in_ctrl, addrgen_out_ctrl) = transpose_config(HEIGHT, WIDTH, TRANSP_MODE) + for channel in range(CHANNELS): + channel_transposed_flat = datamover_execute(input_memory[channel*HEIGHT*WIDTH:(channel+1)*HEIGHT*WIDTH], 1, HEIGHT, WIDTH, addrgen_in_ctrl, addrgen_out_ctrl) + output_memory[channel*HEIGHT*WIDTH:(channel+1)*HEIGHT*WIDTH] = channel_transposed_flat + # output_memory = datamover_execute(input_memory, HEIGHT, WIDTH, addrgen_in_ctrl, addrgen_out_ctrl) + output_matrix = output_memory.reshape(CHANNELS, WIDTH * TRANSP_MODE, HEIGHT // TRANSP_MODE) + elif DATAMOVER_MODE==2: + print(f"\nDATAMOVER_MODE 2: CIM Data Layout Conversion\n") + (addrgen_in_ctrl, addrgen_out_ctrl) = cim_layout_config(HEIGHT, WIDTH, CIM_ROWTILE_SIZE) + for channel in range(CHANNELS): + channel_transposed_flat = datamover_execute(input_memory[channel*HEIGHT*WIDTH:(channel+1)*HEIGHT*WIDTH], 1, HEIGHT, WIDTH, addrgen_in_ctrl, addrgen_out_ctrl) + output_memory[channel*HEIGHT*WIDTH:(channel+1)*HEIGHT*WIDTH] = channel_transposed_flat + output_matrix = output_memory.reshape(CHANNELS, WIDTH // CIM_ROWTILE_SIZE, HEIGHT * CIM_ROWTILE_SIZE) + # elif DATAMOVER_MODE==3: + # output_tensor = cim_transpose(input_memory, ) + elif DATAMOVER_MODE==4: + print(f"\nDATAMOVER_MODE 4: Unfold\n") + (addrgen_in_ctrl, addrgen_out_ctrl) = unfold_config(CHANNELS, HEIGHT, WIDTH, PATCH_SIZE) + output_memory = datamover_execute(input_memory, CHANNELS, HEIGHT, WIDTH, addrgen_in_ctrl, addrgen_out_ctrl) + output_matrix = output_memory.reshape(PATCH_SIZE, int((HEIGHT*WIDTH) / PATCH_SIZE), CHANNELS) # PNC format + else: + raise ValueError(f"Unsupported DATAMOVER_MODE: {DATAMOVER_MODE}") + + print(f"\nOutput Memory (flat):\n{output_memory}\n") + print(f"\nOutput Matrix:\n{output_matrix}\n") + +if __name__ == "__main__": + main() diff --git a/verif/python/generate_stimuli.py b/verif/python/generate_stimuli.py index a0d961a..5b8d71a 100644 --- a/verif/python/generate_stimuli.py +++ b/verif/python/generate_stimuli.py @@ -140,6 +140,8 @@ def write_data_header_file(output_dir, input_matrix, output_matrix, config_param "#include ", "", "// Configuration Parameters", + # f"#define READ_BASE_ADDR {config_params['read_base_addr']}", + # f"#define WRITE_BASE_ADDR {config_params['write_base_addr']}", f"#define BANDWIDTH {config_params['bandwidth']}", f"#define WORD_WIDTH {config_params['word_width']}", f"#define ELEM_WIDTH {config_params['elem_width']}", @@ -150,8 +152,8 @@ def write_data_header_file(output_dir, input_matrix, output_matrix, config_param f"#define CIM_MODE {config_params['cim_mode']}", f"#define CIM_INNER_DIM {config_params['cim_inner_dim']}", f"#define CIM_OUTER_DIM {config_params['cim_outer_dim']}", - f"#define SIZE_M {config_params['matrix_size_m']}", - f"#define SIZE_N {config_params['matrix_size_n']}", + f"#define SIZE_M {config_params['matrix_dim_m']}", + f"#define SIZE_N {config_params['matrix_dim_n']}", "", "uint8_t golden_in [SIZE_M*SIZE_N] = {", ] @@ -170,7 +172,7 @@ def write_data_header_file(output_dir, input_matrix, output_matrix, config_param file.write("\n".join(data_h_string)) return -def transpose(matrix, size_n, size_m, transp_mode): +def transpose(matrix, size_m, size_n, transp_mode): transposed = [[0 for _ in range(size_m * transp_mode)] for _ in range(size_n // transp_mode)] for d1 in range(size_m): for d0 in range(size_n // transp_mode): @@ -179,7 +181,7 @@ def transpose(matrix, size_n, size_m, transp_mode): transposed[d0][(d1*transp_mode)+i] = matrix[d1][(d0*transp_mode)+i] return transposed -def cim_layout(matrix, size_n, size_m, cim_mode, cim_inner_dim, cim_outer_dim): +def cim_layout(matrix, size_m, size_n, cim_mode, cim_inner_dim, cim_outer_dim): if cim_mode == 0: # row-major -> A-Layout row_tile_size = cim_inner_dim initial_size_m = size_m @@ -206,11 +208,27 @@ def cim_layout(matrix, size_n, size_m, cim_mode, cim_inner_dim, cim_outer_dim): cim_matrix[d2][(d1*row_tile_size):(d1*row_tile_size+row_tile_size)] = matrix[d1][d2*(row_tile_size):(d2*row_tile_size+row_tile_size)] # Reshape back to original dimensions for output - print(f"CIM Matrix Size: {len(cim_matrix)} x {len(cim_matrix[0])}") - return cim_matrix +def unfold(tensor, patch_size): + # input tensor must be of shape CHW (channels, height (M), width (N)) + # output tensor produced in shape PNC (patch_size, num_patches, channels) + # patch_size: MobileViT: 2x2 = 4 (must be square) + channels = len(tensor) + height = len(tensor[0]) + width = len(tensor[0][0]) + patch_sidelength = int(math.sqrt(patch_size)) + if (height % patch_sidelength) != 0 or (width % patch_sidelength) != 0: + raise ValueError("[GM] Tensor height and width must be multiples of the patch sidelength.") + num_patches = (height * width) // patch_size # number of patches + tensor_unfolded = [[[0 for _ in range(channels)] for _ in range(num_patches)] for _ in range(patch_size)] + for p in range(patch_size): + for n in range(num_patches): + + tensor_unfolded[p][n] = [ tensor[c][h][w] for c in range(channels)] + + def main(): # Parse command-line arguments parser = argparse.ArgumentParser(description="Memory Read/Write Simulation with Word-Aligned Strides") @@ -226,8 +244,8 @@ def main(): parser.add_argument("--cim_mode", type=int, default=0, help="CIM mode (0=normal, 1=CIM)") parser.add_argument("--cim_inner_dim", type=int, default=4, help="CIM inner dimension") parser.add_argument("--cim_outer_dim", type=int, default=4, help="CIM outer dimension") - parser.add_argument("--matrix_size_m", type=int, default=64, help="Matrix height in elements") - parser.add_argument("--matrix_size_n", type=int, default=64, help="Matrix width in elements") + parser.add_argument("--matrix_dim_m", type=int, default=64, help="Matrix height in elements") + parser.add_argument("--matrix_dim_n", type=int, default=64, help="Matrix width in elements") parser.add_argument("--output_dir", type=str, default="output", help="Directory for storing output files") args = parser.parse_args() @@ -242,15 +260,15 @@ def main(): READ_BASE_ADDR = args.read_base_addr WRITE_BASE_ADDR = args.write_base_addr TRANSP_MODE = args.transp_mode - MATRIX_SIZE_N = args.matrix_size_n - MATRIX_SIZE_M = args.matrix_size_m - TOT_LENGTH = (args.matrix_size_m * args.matrix_size_n) // BANDWIDTH_ELEMS + MATRIX_DIM_N = args.matrix_dim_n + MATRIX_DIM_M = args.matrix_dim_m + TOT_LENGTH = (args.matrix_dim_m * args.matrix_dim_n) // BANDWIDTH_ELEMS OUTPUT_DIR = args.output_dir - if MEMORY_SIZE < ((MATRIX_SIZE_N * MATRIX_SIZE_M * ELEM_WIDTH // WORD_WIDTH) * 2): + if MEMORY_SIZE < ((MATRIX_DIM_N * MATRIX_DIM_M * ELEM_WIDTH // WORD_WIDTH) * 2): raise ValueError(f"MEMORY_SIZE ({MEMORY_SIZE}) is too small for the given matrix size " - f"({MATRIX_SIZE_M}x{MATRIX_SIZE_N}) and element width ({ELEM_WIDTH})") + f"({MATRIX_DIM_M}x{MATRIX_DIM_N}) and element width ({ELEM_WIDTH})") # num_elem_word must be power of two and greater than zero if args.num_elem_word & (args.num_elem_word - 1) != 0 or args.num_elem_word <= 0: raise ValueError("[GM] num_elem_word must be a power of two and greater than zero.") @@ -258,7 +276,7 @@ def main(): if BANDWIDTH_ALIGNED % WORD_SIZE_BITS != 0: raise ValueError("[GM] BANDWIDTH_ALIGNED must be a multiple of the word size (num_elem_word * elem_width).") # # bandwidth width must be a multiple of word size - # if ((MATRIX_SIZE_N * ELEM_WIDTH) < BANDWIDTH_ALIGNED): + # if ((MATRIX_DIM_N * ELEM_WIDTH) < BANDWIDTH_ALIGNED): # raise ValueError("[GM] Matrix width (N) in bits must be at least as large as BANDWIDTH_ALIGNED.") # read_tot_length must not exceed 12-bit register capacity (4096) @@ -266,16 +284,16 @@ def main(): # if ((BANDWIDTH_ALIGNED & (BANDWIDTH_ALIGNED - 1)) != 0) or (BANDWIDTH_ALIGNED < WORD_SIZE_BITS): # raise ValueError(f"[GM] BANDWIDTH_ALIGNED ({BANDWIDTH_ALIGNED}) must be a power of 2 and greater than the WORD_SIZE ({WORD_SIZE_BITS}).") - if ((TOT_LENGTH >= 4096) & (args.datamover_mode != 0)): - raise ValueError("[GM] TOT_LENGTH (MxN / BW_ELEM) must be less than 4096 in transpose and CIM modes (12-bit register limit).") + # if ((TOT_LENGTH >= 4096) & (args.datamover_mode != 0)): + # raise ValueError("[GM] TOT_LENGTH (MxN / BW_ELEM) must be less than 4096 in transpose and CIM modes (12-bit register limit).") if(args.datamover_mode == 1): # transpose mode # transp_mode must be valid (1=1elem, 2=2elem, 4=4elem) if args.transp_mode not in [1, 2, 4]: raise ValueError("[GM] transp_mode must be 1 (1 elem), 2 (2 elem), or 4 (4 elem).") - if (MATRIX_SIZE_N % args.transp_mode) != 0: - raise ValueError(f"[GM] Matrix width N ({MATRIX_SIZE_N}) must be a multiple of transp_mode ({args.transp_mode}).") + if (MATRIX_DIM_N % args.transp_mode) != 0: + raise ValueError(f"[GM] Matrix width N ({MATRIX_DIM_N}) must be a multiple of transp_mode ({args.transp_mode}).") print(f"Memory Size: {MEMORY_SIZE} entries") print(f"Word Size: {WORD_SIZE_BITS} bits") @@ -292,11 +310,11 @@ def main(): memory_flat = convert_memory_to_vector(memory, ELEM_WIDTH, WORD_WIDTH) # Extract matrix (read dimensions) from memory - input_matrix = [[0 for _ in range(MATRIX_SIZE_N)] for _ in range(MATRIX_SIZE_M)] - for d1 in range(MATRIX_SIZE_M): + input_matrix = [[0 for _ in range(MATRIX_DIM_N)] for _ in range(MATRIX_DIM_M)] + for d1 in range(MATRIX_DIM_M): row = [] - for d0 in range(MATRIX_SIZE_N): - input_matrix[d1][d0] = memory_flat[(READ_BASE_ADDR + d1 * MATRIX_SIZE_N + d0)] + for d0 in range(MATRIX_DIM_N): + input_matrix[d1][d0] = memory_flat[(READ_BASE_ADDR + d1 * MATRIX_DIM_N + d0)] # Print input matrix print("Input Matrix:") @@ -309,11 +327,11 @@ def main(): if args.datamover_mode == 0: # Copy mode output_matrix = input_matrix elif args.datamover_mode == 1: # Transpose mode - output_matrix = transpose(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, TRANSP_MODE) + output_matrix = transpose(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, TRANSP_MODE) elif args.datamover_mode == 2: # CIM mode - output_matrix = cim_layout(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + output_matrix = cim_layout(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) # elif args.datamover_mode == 2: # TEST CIM mode (TWICE: should be identical to input) - # intermediate_matrix = cim_layout(input_matrix, MATRIX_SIZE_N, MATRIX_SIZE_M, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + # intermediate_matrix = cim_layout(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) # intermediate_size_m = len(intermediate_matrix) # intermediate_size_n = len(intermediate_matrix[0]) # # Print intermediate matrix @@ -321,11 +339,11 @@ def main(): # for i, row in enumerate(intermediate_matrix): # print(f"Row {i}: {[format(elem, 'X') for elem in row]}") # print("\n") - # output_matrix = cim_layout(intermediate_matrix, intermediate_size_n, intermediate_size_m, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + # output_matrix = cim_layout(intermediate_matrix, intermediate_size_m, intermediate_size_n, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) elif args.datamover_mode == 3: # CIM layout transpose mode (INPUT SIZES EXPECTED IN ORIGINAL (ROW-MAJOR) LAYOUT FORM!) print("TRANSP_MODE = ", TRANSP_MODE) - converted_size_m = MATRIX_SIZE_N // args.cim_inner_dim - converted_size_n = MATRIX_SIZE_M * args.cim_inner_dim + converted_size_m = MATRIX_DIM_N // args.cim_inner_dim + converted_size_n = MATRIX_DIM_M * args.cim_inner_dim # Reshape input_matrix to converted_size_m x converted_size_n input_flat = [elem for row in input_matrix for elem in row] reshaped_matrix = [ @@ -336,7 +354,7 @@ def main(): print(f"Row {i}: {[format(elem, 'X') for elem in row]}") print("\n") - intermediate1_matrix = cim_layout(reshaped_matrix, converted_size_n, converted_size_m, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + intermediate1_matrix = cim_layout(reshaped_matrix, converted_size_m, converted_size_n, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) intermediate1_size_m = len(intermediate1_matrix) intermediate1_size_n = len(intermediate1_matrix[0]) @@ -344,14 +362,14 @@ def main(): for i, row in enumerate(intermediate1_matrix): print(f"Row {i}: {[format(elem, 'X') for elem in row]}") print("\n") - intermediate2_matrix = transpose(intermediate1_matrix, intermediate1_size_n, intermediate1_size_m, TRANSP_MODE) + intermediate2_matrix = transpose(intermediate1_matrix, intermediate1_size_m, intermediate1_size_n, TRANSP_MODE) intermediate2_size_m = len(intermediate2_matrix) intermediate2_size_n = len(intermediate2_matrix[0]) print("\nIntermediate2 Matrix (row major transpose):") for i, row in enumerate(intermediate2_matrix): print(f"Row {i}: {[format(elem, 'X') for elem in row]}") print("\n") - output_matrix = cim_layout(intermediate2_matrix, intermediate2_size_n, intermediate2_size_m, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + output_matrix = cim_layout(intermediate2_matrix, intermediate2_size_m, intermediate2_size_n, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) else: raise ValueError("[GM] datamover_mode must be 0 (copy), 1 (transpose), or 2 (CIM).") @@ -375,6 +393,8 @@ def main(): # Write data header file for C testing config_params = { + # 'read_base_addr': args.read_base_addr, + # 'write_base_addr': args.write_base_addr, 'bandwidth': args.bandwidth_bits, 'word_width': WORD_WIDTH, 'elem_width': args.elem_width, @@ -385,8 +405,8 @@ def main(): 'cim_mode': args.cim_mode, 'cim_inner_dim': args.cim_inner_dim, 'cim_outer_dim': args.cim_outer_dim, - 'matrix_size_m': args.matrix_size_m, - 'matrix_size_n': args.matrix_size_n + 'matrix_dim_m': args.matrix_dim_m, + 'matrix_dim_n': args.matrix_dim_n } write_data_header_file(OUTPUT_DIR, input_matrix, output_matrix, config_params) diff --git a/verif/python/validate_config.py b/verif/python/validate_config.py index afce8b7..a5ae2ca 100755 --- a/verif/python/validate_config.py +++ b/verif/python/validate_config.py @@ -9,7 +9,7 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, misaligned_accesses, datamover_mode, transp_mode, cim_mode, cim_inner_dim, cim_outer_dim, - matrix_size_m, matrix_size_n): + matrix_dim_m, matrix_dim_n): """Validate configuration parameters""" errors = [] warnings = [] @@ -27,9 +27,9 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, misaligned_a if word_width % elem_width != 0: errors.append(f"WORD_WIDTH ({word_width}) must be divisible by ELEM_WIDTH ({elem_width})") - if memory_size < (matrix_size_n * matrix_size_m * elem_width // word_width) * 2: + if memory_size < (matrix_dim_n * matrix_dim_m * elem_width // word_width) * 2: errors.append(f"MEMORY_SIZE ({memory_size}) is too small for the given matrix size " - f"({matrix_size_m}x{matrix_size_n}) and element width ({elem_width})") + f"({matrix_dim_m}x{matrix_dim_n}) and element width ({elem_width})") # Mode validation (based on config.mk) if datamover_mode not in [0, 1, 2, 3]: @@ -53,27 +53,27 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, misaligned_a errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) must be a multiple of bandwidth ({bandwidth_elems})") if (cim_outer_dim % bandwidth_elems != 0) and (cim_mode == 1): errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) must be a multiple of bandwidth ({bandwidth_elems})") - if (cim_inner_dim > matrix_size_n) and (cim_mode == 0): - errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) cannot be greater than matrix width ({matrix_size_n})") - if (cim_outer_dim > matrix_size_m) and (cim_mode == 1): - errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) cannot be greater than matrix height ({matrix_size_m})") + if (cim_inner_dim > matrix_dim_n) and (cim_mode == 0): + errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) cannot be greater than matrix width ({matrix_dim_n})") + if (cim_outer_dim > matrix_dim_m) and (cim_mode == 1): + errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) cannot be greater than matrix height ({matrix_dim_m})") # Memory requirements - matrix_elements = matrix_size_m * matrix_size_n + matrix_elements = matrix_dim_m * matrix_dim_n matrix_words = (matrix_elements * elem_width + word_width - 1) // word_width total_memory_needed = matrix_words * 2 # Input + output matrices if total_memory_needed > memory_size: errors.append(f"Memory size ({memory_size} words) insufficient for matrices " - f"({total_memory_needed} words needed for {matrix_size_m}x{matrix_size_n} input+output)") + f"({total_memory_needed} words needed for {matrix_dim_m}x{matrix_dim_n} input+output)") # Matrix dimension alignment errors - # if matrix_size_n % bandwidth_elems != 0: - # errors.append(f"Matrix width ({matrix_size_n}) not aligned to bandwidth " + # if matrix_dim_n % bandwidth_elems != 0: + # errors.append(f"Matrix width ({matrix_dim_n}) not aligned to bandwidth " # f"({bandwidth_elems} elements)") - # if matrix_size_m % bandwidth_elems != 0: - # errors.append(f"Matrix height ({matrix_size_m}) not aligned to bandwidth " + # if matrix_dim_m % bandwidth_elems != 0: + # errors.append(f"Matrix height ({matrix_dim_m}) not aligned to bandwidth " # f"({bandwidth_elems} elements)") # Transpose-specific validation @@ -96,8 +96,8 @@ def main(): parser.add_argument("--cim_mode", type=int, required=True) parser.add_argument("--cim_inner_dim", type=int, required=True) parser.add_argument("--cim_outer_dim", type=int, required=True) - parser.add_argument("--matrix_size_m", type=int, required=True) - parser.add_argument("--matrix_size_n", type=int, required=True) + parser.add_argument("--matrix_dim_m", type=int, required=True) + parser.add_argument("--matrix_dim_n", type=int, required=True) args = parser.parse_args() @@ -105,7 +105,7 @@ def main(): args.bandwidth, args.word_width, args.elem_width, args.memory_size, args.misaligned_accesses, args.datamover_mode, args.transp_mode, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, - args.matrix_size_m, args.matrix_size_n + args.matrix_dim_m, args.matrix_dim_n ) # Print results @@ -141,7 +141,7 @@ def main(): bandwidth_aligned = args.bandwidth - (args.word_width if args.misaligned_accesses else 0) bandwidth_elems = bandwidth_aligned // args.elem_width num_elem_word = args.word_width // args.elem_width - matrix_words = (args.matrix_size_m * args.matrix_size_n) // num_elem_word + matrix_words = (args.matrix_dim_m * args.matrix_dim_n) // num_elem_word print(f"\nComputed values:") print(f" Elements per bandwidth: {bandwidth_elems}") diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index cb8b333..0f569eb 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -62,9 +62,11 @@ import tb_package::*; logic [PERIPH_ID-1:0] periph_r_id; logic [2:0] transp_mode; - logic [15:0] transp_len; - logic [11:0] matrix_size_m; - logic [11:0] matrix_size_n; + // logic [15:0] transp_len; + logic [11:0] matrix_dim_m; + logic [11:0] matrix_dim_n; + logic [3:0] read_dim_enable; + logic [3:0] write_dim_enable; // Performs one entire clock cycle. task cycle; @@ -105,25 +107,29 @@ import tb_package::*; typedef struct { logic [31:0] base_addr; - logic [31:0] d0_stride; - logic [31:0] d1_stride; - logic [31:0] d2_stride; - logic [11:0] d0_length; - logic [11:0] d1_length; - logic [11:0] tot_length; + logic [15:0] d0_stride; + logic [15:0] d1_stride; + logic [15:0] d2_stride; + logic [15:0] d3_stride; + logic [15:0] d4_stride; + logic [15:0] d0_length; + logic [15:0] d1_length; + logic [15:0] d2_length; + logic [15:0] d3_length; + logic [31:0] tot_length; } addressgen_t; addressgen_t read_addr, write_addr; - assign read_addr = '{`STIM_READ_BASE_ADDR, `STIM_READ_D0_STRIDE, `STIM_READ_D1_STRIDE, `STIM_READ_D2_STRIDE, `STIM_READ_D0_LENGTH, `STIM_READ_D1_LENGTH, `STIM_READ_TOT_LENGTH}; - assign write_addr = '{`STIM_WRITE_BASE_ADDR, `STIM_WRITE_D0_STRIDE, `STIM_WRITE_D1_STRIDE, `STIM_WRITE_D2_STRIDE, `STIM_WRITE_D0_LENGTH, `STIM_WRITE_D1_LENGTH, `STIM_WRITE_TOT_LENGTH}; - // assign read_addr = '{`STIM_READ_BASE_ADDR, 32'h4, 32'h10, 32'h4, 32'h4, 32'h10}; - // assign write_addr = '{32'h40, 32'h4, 32'h10, 32'h4, 32'h4, 32'h10}; + assign read_addr = '{`STIM_READ_BASE_ADDR, `STIM_READ_D0_STRIDE, `STIM_READ_D1_STRIDE, `STIM_READ_D2_STRIDE, `STIM_READ_D3_STRIDE, `STIM_READ_D4_STRIDE, `STIM_READ_D0_LENGTH, `STIM_READ_D1_LENGTH, `STIM_READ_D2_LENGTH, `STIM_READ_D3_LENGTH, `STIM_READ_TOT_LENGTH}; + assign write_addr = '{`STIM_WRITE_BASE_ADDR, `STIM_WRITE_D0_STRIDE, `STIM_WRITE_D1_STRIDE, `STIM_WRITE_D2_STRIDE, `STIM_WRITE_D3_STRIDE, `STIM_WRITE_D4_STRIDE, `STIM_WRITE_D0_LENGTH, `STIM_WRITE_D1_LENGTH, `STIM_WRITE_D2_LENGTH, `STIM_WRITE_D3_LENGTH, `STIM_WRITE_TOT_LENGTH}; assign transp_mode = `STIM_TRANSP_MODE; - assign transp_len = `STIM_TRANSP_LEN; - assign matrix_size_m = `STIM_MATRIX_SIZE_M; - assign matrix_size_n = `STIM_MATRIX_SIZE_N; + // assign transp_len = `STIM_TRANSP_LEN; + assign matrix_dim_m = `STIM_MATRIX_DIM_M; + assign matrix_dim_n = `STIM_MATRIX_DIM_N; + assign read_dim_enable = `STIM_READ_DIM_ENABLE; + assign write_dim_enable = `STIM_WRITE_DIM_ENABLE; datamover_top_wrap #( @@ -227,10 +233,8 @@ import tb_package::*; int error_status; initial begin : main_execution - logic [31:0] len0_reg; - logic [31:0] len1_reg; - logic [31:0] transp_mode_reg; - logic [31:0] matrix_size_reg; + logic [31:0] ctrl_engine_reg; + logic [31:0] dim_enable_reg; $info("Start execution...\n"); @@ -260,34 +264,29 @@ import tb_package::*; periph_write(datamover_package::DATAMOVER_REG_OUT_PTR, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.base_addr, clk_i, periph_bus); // Configure packed length registers (see datamover_package.sv) - len0_reg = {read_addr.d1_length[7:0], read_addr.d0_length[11:0], read_addr.tot_length[11:0]}; - len1_reg = {4'b0, read_addr.d1_length[11:8], write_addr.d1_length[11:0], write_addr.d0_length[11:0]}; - transp_mode_reg = {transp_len, 13'b0, transp_mode}; // ToDo(cdurrer): Leftover = transp_len??? - matrix_size_reg = {8'b0, matrix_size_n[11:0], matrix_size_m[11:0]}; + ctrl_engine_reg = {5'b0, matrix_dim_n[11:0], matrix_dim_m[11:0], transp_mode[2:0]}; + dim_enable_reg = {24'b0, write_dim_enable[3:0], read_dim_enable[3:0]}; // Make sure tot_length is the same for read and write assert (read_addr.tot_length == write_addr.tot_length) else $fatal("Read and write total lengths do not match!"); - periph_write(datamover_package::DATAMOVER_REG_LEN0, datamover_package::DATAMOVER_REGISTER_OFFS, len0_reg, clk_i, periph_bus); - periph_write(datamover_package::DATAMOVER_REG_LEN1, datamover_package::DATAMOVER_REGISTER_OFFS, len1_reg, clk_i, periph_bus); - - periph_write(datamover_package::DATAMOVER_REG_IN_D0_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, read_addr.d0_stride, clk_i, periph_bus); - periph_write(datamover_package::DATAMOVER_REG_IN_D1_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, read_addr.d1_stride, clk_i, periph_bus); - periph_write(datamover_package::DATAMOVER_REG_IN_D2_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, read_addr.d2_stride, clk_i, periph_bus); - - periph_write(datamover_package::DATAMOVER_REG_OUT_D0_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.d0_stride, clk_i, periph_bus); - periph_write(datamover_package::DATAMOVER_REG_OUT_D1_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.d1_stride, clk_i, periph_bus); - periph_write(datamover_package::DATAMOVER_REG_OUT_D2_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.d2_stride, clk_i, periph_bus); - - // Transposition mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) - periph_write(datamover_package::DATAMOVER_REG_TRANSP_MODE, datamover_package::DATAMOVER_REGISTER_OFFS, transp_mode_reg, clk_i, periph_bus); - - periph_write(datamover_package::DATAMOVER_REG_MATRIX_SIZE, datamover_package::DATAMOVER_REGISTER_OFFS, matrix_size_reg, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_TOT_LEN, datamover_package::DATAMOVER_REGISTER_OFFS, read_addr.tot_length, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_IN_D0, datamover_package::DATAMOVER_REGISTER_OFFS, {read_addr.d0_stride, read_addr.d0_length}, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_IN_D1, datamover_package::DATAMOVER_REGISTER_OFFS, {read_addr.d1_stride, read_addr.d1_length}, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_IN_D2, datamover_package::DATAMOVER_REGISTER_OFFS, {read_addr.d2_stride, read_addr.d2_length}, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_IN_D3, datamover_package::DATAMOVER_REGISTER_OFFS, {read_addr.d3_stride, read_addr.d3_length}, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_OUT_D0, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d0_stride, write_addr.d0_length}, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_OUT_D1, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d1_stride, write_addr.d1_length}, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_OUT_D2, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d2_stride, write_addr.d2_length}, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_OUT_D3, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d3_stride, write_addr.d3_length}, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_IN_OUT_D4_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d4_stride, read_addr.d4_stride}, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_DIM_ENABLE, datamover_package::DATAMOVER_REGISTER_OFFS, dim_enable_reg, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_CTRL_ENGINE, datamover_package::DATAMOVER_REGISTER_OFFS, ctrl_engine_reg, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_COMMIT_AND_TRIGGER, datamover_package::HWPE_REGISTER_OFFS, 32'h0, clk_i, periph_bus); while(status == 32'h00) - periph_read(datamover_package::DATAMOVER_STATUS, datamover_package::HWPE_REGISTER_OFFS, status, clk_i, periph_bus); + periph_read(datamover_package::DATAMOVER_STATUS, datamover_package::HWPE_REGISTER_OFFS, status, clk_i, periph_bus); // ToDo(cdurrer): Why STATUS and not FINISHED register? $info("Datamover working...\n"); @@ -298,7 +297,7 @@ import tb_package::*; check_output( GOLDEN_PATH, // File containing golden reference data - 32'h0, // Start address in memory + 32'h0, // Start address in memory MEMORY_SIZE, // Number of entries to check tb_datamover_top_wrap.i_testbench_memory.memory, // Reference to memory array error_status From 13d42d5769bfa9f128c7cb40c9145b12912b738e Mon Sep 17 00:00:00 2001 From: cdurrer Date: Thu, 19 Mar 2026 14:11:18 +0100 Subject: [PATCH 26/29] [rtl] Implemented partial tile handling for 1-element transpose (limitation: matrix N-size needs to be word-aligned) --- rtl/datamover_engine.sv | 63 +++++++++++++++++++++++++++++++--------- rtl/datamover_package.sv | 3 +- rtl/datamover_top.sv | 1 + 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/rtl/datamover_engine.sv b/rtl/datamover_engine.sv index e165e1d..63c54e0 100644 --- a/rtl/datamover_engine.sv +++ b/rtl/datamover_engine.sv @@ -14,6 +14,7 @@ /* * Authors: Francesco Conti * Sergio Mazzola + * Cyrill Durrer */ module datamover_engine @@ -22,7 +23,7 @@ module datamover_engine import datamover_package::*; #( parameter int unsigned FIFO_DEPTH = 2, - parameter int unsigned BANDWIDTH_ALIGNED = 32, + parameter int unsigned BANDWIDTH_ALIGNED = 512, parameter int unsigned NUM_ELEM_WORD = 4, // number of elements in a bank word parameter int unsigned ELEM_WIDTH = 8, // element width (in bits) // Dependent parameters: do not modify! @@ -46,7 +47,7 @@ module datamover_engine // number of elements (in the full bandwidth, not a single bank word) localparam NB_ELEMENTS = BANDWIDTH_ALIGNED / ELEM_WIDTH; logic [23:0] matrix_tot_size; - assign matrix_tot_size = ctrl_i.matrix_dim_m * ctrl_i.matrix_dim_n; // ToDo(cdurrer): additional MUL, problem? + assign matrix_tot_size = ctrl_i.matrix_dim_m * ctrl_i.matrix_dim_n; // ToDo(cdurrer): additional MUL, problem? (Could also be configured in control register by the HAL) logic [$clog2(NB_ELEMENTS)-1:0] remaining_elems; assign remaining_elems = matrix_tot_size % NB_ELEMENTS; logic [11:0] nof_accesses; @@ -54,17 +55,20 @@ module datamover_engine // Type def and internal signals typedef enum logic { WRITE, READ } datamover_engine_fsm_t; - datamover_engine_fsm_t fsm_d, fsm_q; - logic clear_elem_matrix; - logic [$clog2(NB_ELEMENTS):0] cnt_q, cnt_d; - logic [11:0] tot_cnt_q, tot_cnt_d; // ToDo(cdurrer): bitwidth? - logic cnt_en; + datamover_engine_fsm_t fsm_d, fsm_q; + logic clear_elem_matrix; + logic [$clog2(NB_ELEMENTS):0] cnt_q, cnt_d; + logic [11:0] tot_cnt_q, tot_cnt_d; // ToDo(cdurrer): bitwidth? + logic cnt_en; logic [NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] data_in_unrolled; - logic data_in_valid; - logic data_in_ready; + logic data_in_valid; + logic data_in_ready; logic [NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] data_out_unrolled; - logic data_out_valid; - logic data_out_ready; + logic data_out_valid; + logic data_out_ready; + logic [11:0] m_tiles, n_tiles, m_elem_cnt, n_tile_cnt; // ToDo(cdurrer): review bitwidths + logic [$clog2(NB_ELEMENTS):0] leftover_rows, leftover_cols; + logic last_m_tile, last_n_tile; // FSM: WRITE -> READ on input handshake at end of write, READ -> WRITE on output handshake at end of read always_comb @@ -144,8 +148,41 @@ module datamover_engine .pop_o ( data_out ) ); - // handle last transfer with partial strobe if misaligned - assign data_out_prefifo.strb = ((tot_cnt_q >= nof_accesses-1) && (remaining_elems != 0)) ? (1 << remaining_elems) - 1 : '1; + + // Partial tile / leftover elements handling + // Due to the streamer address generation, matrices need to be word-aligned in n-dimension for transposition + assign m_tiles = (ctrl_i.matrix_dim_m + NB_ELEMENTS - 1) / NB_ELEMENTS; + assign n_tiles = (ctrl_i.matrix_dim_n + NB_ELEMENTS - 1) / NB_ELEMENTS; + assign leftover_rows = ctrl_i.matrix_dim_m % NB_ELEMENTS; + assign leftover_cols = ctrl_i.matrix_dim_n % NB_ELEMENTS; + assign m_elem_cnt = tot_cnt_q % ((m_tiles) * NB_ELEMENTS); + assign n_tile_cnt = tot_cnt_q / ((m_tiles) * NB_ELEMENTS); + assign last_m_tile = (m_elem_cnt / NB_ELEMENTS >= ctrl_i.matrix_dim_m / NB_ELEMENTS); + assign last_n_tile = (n_tile_cnt >= (ctrl_i.matrix_dim_n / NB_ELEMENTS)); + + always_comb begin + if(ctrl_i.datamover_mode == 0) begin // copy mode + data_out_prefifo.strb = ((tot_cnt_q >= nof_accesses-1) && (remaining_elems != 0)) ? (1 << remaining_elems) - 1 : '1; + end else if(ctrl_i.datamover_mode == 1) begin // transpose mode + if((last_m_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin + if(m_elem_cnt % NB_ELEMENTS < leftover_cols) begin + data_out_prefifo.strb = (1 << leftover_rows) - 1; + end else begin + data_out_prefifo.strb = '0; + end + end else if(last_m_tile && leftover_rows != 0) begin + data_out_prefifo.strb = (1 << leftover_rows) - 1; + end else if(last_n_tile && leftover_cols != 0) begin + if(m_elem_cnt % NB_ELEMENTS < leftover_cols) begin + data_out_prefifo.strb = '1; + end else begin + data_out_prefifo.strb = '0; + end + end else begin + data_out_prefifo.strb = '1; + end + end + end assign data_out_prefifo.data = data_out_unrolled; assign data_out_prefifo.valid = data_out_valid; diff --git a/rtl/datamover_package.sv b/rtl/datamover_package.sv index 4ced8c4..bbda557 100644 --- a/rtl/datamover_package.sv +++ b/rtl/datamover_package.sv @@ -36,6 +36,7 @@ package datamover_package; transp_mode_e transp_mode; logic [$clog2(MAX_BANDWIDTH/8):0] transp_len; logic [2:0] transp_stride; // 1, 2, or 4 + logic [4:0] datamover_mode; // 0: copy, 1: tranpose, 2: CIM layout conversion logic [11:0] matrix_dim_m; logic [11:0] matrix_dim_n; } ctrl_engine_t; @@ -70,7 +71,7 @@ package datamover_package; parameter int unsigned DATAMOVER_REG_OUT_D3 = 32'h28; // [31:16] out_d3_stride; [15:0] out_d3_len parameter int unsigned DATAMOVER_REG_IN_OUT_D4_STRIDE = 32'h2C; // [31:16] out_d4_stride; [15:0] in_d4_stride (d4_len unnecessary due to tot_len) parameter int unsigned DATAMOVER_REG_DIM_ENABLE = 32'h30; // [31:8] unused;[7:4] write_dim_en; [3:0] read_dim_en -> one-hot encoding (LSB->d1), d0 is always enabled - parameter int unsigned DATAMOVER_REG_CTRL_ENGINE = 32'h34; // [31:27] unused; [26:15] matrix_dim_n; [14:3] matrix_dim_m [2:0] transp_mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) + parameter int unsigned DATAMOVER_REG_CTRL_ENGINE = 32'h34; // [31:27] datamover_mode (0: copy, 1: transpose, 2: CIM layout conversion); [26:15] matrix_dim_n; [14:3] matrix_dim_m [2:0] transp_mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) // Note: increase N_IO_REGS in datamover_top.sv when adding new registers here! diff --git a/rtl/datamover_top.sv b/rtl/datamover_top.sv index 016a19e..3d9fd39 100644 --- a/rtl/datamover_top.sv +++ b/rtl/datamover_top.sv @@ -249,6 +249,7 @@ module datamover_top engine_ctrl.transp_stride = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b000 ? 1 : reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b001 ? 1 : reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b010 ? 2 : 4; + engine_ctrl.datamover_mode = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][31:27]; engine_ctrl.matrix_dim_m = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][14:3]; engine_ctrl.matrix_dim_n = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][26:15]; engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; // ToDo(cdurrer): What exactly did transp_len do? Use it to handle leftovers? From 6116f1b4af9a2d193b22ac593472f472e4a94ebd Mon Sep 17 00:00:00 2001 From: cdurrer Date: Fri, 20 Mar 2026 15:11:02 +0100 Subject: [PATCH 27/29] [rtl, golden model] Introduced CIM layout conversion and reverse modes, including partial tile handling --- config.mk | 6 +- rtl/datamover_engine.sv | 76 ++++++++++------ verif/python/generate_stimuli.py | 143 ++++++++++++++++--------------- verif/python/validate_config.py | 10 +-- 4 files changed, 132 insertions(+), 103 deletions(-) diff --git a/config.mk b/config.mk index e810d2e..3dba9c4 100644 --- a/config.mk +++ b/config.mk @@ -16,17 +16,17 @@ BANDWIDTH ?= 512 # in bits, multiple of WORD_WIDTH (512) WORD_WIDTH ?= 64 # in bits, multiple of ELEM_WIDTH (64) ELEM_WIDTH ?= 8 # in bits (8) -MEMORY_SIZE ?= 65536 # in words +MEMORY_SIZE ?= 131072 # in words MISALIGNED_ACCESSES ?= 0 DATAMOVER_MODE ?= 1 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion, 3 = CIM data layout transpose TRANSP_MODE ?= 1 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted -CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> A-Layout, 1: row-major -> B-Layout +CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> A-Layout, 1: reverse (A-Layout -> row-major), ToDo (unnecessary for 64x64 CIM): 2: row-major -> B-Layout, 3: reverse (B-Layout -> row-major) CIM_INNER_DIM ?= 64 # Inner dimension of the CIM accelerator (in elements): 64 for 64x8 CIM macro CIM_OUTER_DIM ?= 64 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros # Input matrix dimensions (in elements) -MATRIX_DIM_M ?= 64 # Matrix height in elements +MATRIX_DIM_M ?= 64 # Matrix height in elements MATRIX_DIM_N ?= 64 # Matrix width in elements READ_BASE_ADDR = 0 diff --git a/rtl/datamover_engine.sv b/rtl/datamover_engine.sv index 63c54e0..7b35f70 100644 --- a/rtl/datamover_engine.sv +++ b/rtl/datamover_engine.sv @@ -32,9 +32,9 @@ module datamover_engine // global signals input logic clk_i, input logic rst_ni, - input logic test_mode_i, + input logic test_mode_i, // ToDo: unused // local enable & clear - input logic enable_i, + input logic enable_i, // ToDo: unused input logic clear_i, // control registers input ctrl_engine_t ctrl_i, @@ -46,19 +46,21 @@ module datamover_engine // number of elements (in the full bandwidth, not a single bank word) localparam NB_ELEMENTS = BANDWIDTH_ALIGNED / ELEM_WIDTH; + localparam int unsigned NB_ELEM_LOG2 = $clog2(NB_ELEMENTS); + logic [23:0] matrix_tot_size; assign matrix_tot_size = ctrl_i.matrix_dim_m * ctrl_i.matrix_dim_n; // ToDo(cdurrer): additional MUL, problem? (Could also be configured in control register by the HAL) - logic [$clog2(NB_ELEMENTS)-1:0] remaining_elems; - assign remaining_elems = matrix_tot_size % NB_ELEMENTS; + logic [NB_ELEM_LOG2-1:0] remaining_elems; + assign remaining_elems = matrix_tot_size & (NB_ELEMENTS - 1); // modulo (NB_ELEMENTS: power of two) logic [11:0] nof_accesses; - assign nof_accesses = (matrix_tot_size / NB_ELEMENTS) + ((remaining_elems != 0) ? 1 : 0); + assign nof_accesses = (matrix_tot_size >> NB_ELEM_LOG2) + ((remaining_elems != 0) ? 1 : 0); // Type def and internal signals typedef enum logic { WRITE, READ } datamover_engine_fsm_t; datamover_engine_fsm_t fsm_d, fsm_q; logic clear_elem_matrix; - logic [$clog2(NB_ELEMENTS):0] cnt_q, cnt_d; - logic [11:0] tot_cnt_q, tot_cnt_d; // ToDo(cdurrer): bitwidth? + logic [NB_ELEM_LOG2:0] cnt_q, cnt_d; + logic [15:0] tot_cnt_q, tot_cnt_d; // ToDo(cdurrer): bitwidth? logic cnt_en; logic [NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] data_in_unrolled; logic data_in_valid; @@ -66,8 +68,8 @@ module datamover_engine logic [NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] data_out_unrolled; logic data_out_valid; logic data_out_ready; - logic [11:0] m_tiles, n_tiles, m_elem_cnt, n_tile_cnt; // ToDo(cdurrer): review bitwidths - logic [$clog2(NB_ELEMENTS):0] leftover_rows, leftover_cols; + logic [11:0] m_tiles, n_tile_cnt, m_elem_cnt,expanded_m_elems; // ToDo(cdurrer): review bitwidths + logic [NB_ELEM_LOG2:0] leftover_rows, leftover_cols; logic last_m_tile, last_n_tile; // FSM: WRITE -> READ on input handshake at end of write, READ -> WRITE on output handshake at end of read @@ -151,29 +153,31 @@ module datamover_engine // Partial tile / leftover elements handling // Due to the streamer address generation, matrices need to be word-aligned in n-dimension for transposition - assign m_tiles = (ctrl_i.matrix_dim_m + NB_ELEMENTS - 1) / NB_ELEMENTS; - assign n_tiles = (ctrl_i.matrix_dim_n + NB_ELEMENTS - 1) / NB_ELEMENTS; - assign leftover_rows = ctrl_i.matrix_dim_m % NB_ELEMENTS; - assign leftover_cols = ctrl_i.matrix_dim_n % NB_ELEMENTS; - assign m_elem_cnt = tot_cnt_q % ((m_tiles) * NB_ELEMENTS); - assign n_tile_cnt = tot_cnt_q / ((m_tiles) * NB_ELEMENTS); - assign last_m_tile = (m_elem_cnt / NB_ELEMENTS >= ctrl_i.matrix_dim_m / NB_ELEMENTS); - assign last_n_tile = (n_tile_cnt >= (ctrl_i.matrix_dim_n / NB_ELEMENTS)); + localparam logic [NB_ELEMENTS-1:0] STRB_ONE = {{(NB_ELEMENTS-1){1'b0}}, 1'b1}; // Necessary to force the shifting operation to the correct bitwidth (default would be only 32b) + assign m_tiles = (ctrl_i.matrix_dim_m + NB_ELEMENTS - 1) >> NB_ELEM_LOG2; // ceil division + // assign n_tiles = (ctrl_i.matrix_dim_n + NB_ELEMENTS - 1) / NB_ELEMENTS; + assign leftover_rows = ctrl_i.matrix_dim_m & (NB_ELEMENTS - 1); + assign leftover_cols = ctrl_i.matrix_dim_n & (NB_ELEMENTS - 1); + assign expanded_m_elems = m_tiles * NB_ELEMENTS; + assign m_elem_cnt = (expanded_m_elems == 0) ? '0 : (tot_cnt_q % expanded_m_elems); + assign n_tile_cnt = (expanded_m_elems == 0) ? '0 : (tot_cnt_q / expanded_m_elems); + assign last_m_tile = ((m_elem_cnt >> NB_ELEM_LOG2) >= (ctrl_i.matrix_dim_m >> NB_ELEM_LOG2)); + assign last_n_tile = (n_tile_cnt >= (ctrl_i.matrix_dim_n >> NB_ELEM_LOG2)); always_comb begin - if(ctrl_i.datamover_mode == 0) begin // copy mode - data_out_prefifo.strb = ((tot_cnt_q >= nof_accesses-1) && (remaining_elems != 0)) ? (1 << remaining_elems) - 1 : '1; - end else if(ctrl_i.datamover_mode == 1) begin // transpose mode + if(ctrl_i.datamover_mode == 0) begin // Copy mode + data_out_prefifo.strb = ((tot_cnt_q >= nof_accesses-1) && (remaining_elems != 0)) ? ((STRB_ONE << remaining_elems) - 1) : '1; + end else if(ctrl_i.datamover_mode == 1) begin // Transpose mode if((last_m_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin - if(m_elem_cnt % NB_ELEMENTS < leftover_cols) begin - data_out_prefifo.strb = (1 << leftover_rows) - 1; + if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin + data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); end else begin data_out_prefifo.strb = '0; end end else if(last_m_tile && leftover_rows != 0) begin - data_out_prefifo.strb = (1 << leftover_rows) - 1; + data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); end else if(last_n_tile && leftover_cols != 0) begin - if(m_elem_cnt % NB_ELEMENTS < leftover_cols) begin + if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin data_out_prefifo.strb = '1; end else begin data_out_prefifo.strb = '0; @@ -181,6 +185,26 @@ module datamover_engine end else begin data_out_prefifo.strb = '1; end + end else if(ctrl_i.datamover_mode == 2) begin // CIM layout conversion mode + if((last_m_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin + if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + data_out_prefifo.strb = ((STRB_ONE << leftover_cols) - 1); + end else begin + data_out_prefifo.strb = '0; + end + end else if(last_m_tile && leftover_rows != 0) begin + if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + data_out_prefifo.strb = '1; + end else begin + data_out_prefifo.strb = '0; + end + end else if(last_n_tile && leftover_cols != 0) begin + data_out_prefifo.strb = ((STRB_ONE << leftover_cols) - 1); + end else begin + data_out_prefifo.strb = '1; + end + end else begin + data_out_prefifo.strb = '1; // ToDo: Could be set to 0 if all cases are handled properly end end @@ -208,7 +232,7 @@ module datamover_engine assign cnt_en = fsm_q == WRITE ? data_in_valid & data_in_ready : data_out_valid & data_out_ready; // count total number of write accesses - assign tot_cnt_d = (tot_cnt_q < matrix_tot_size) && (data_out_prefifo.valid) ? tot_cnt_q + 1 : tot_cnt_q; + assign tot_cnt_d = (tot_cnt_q < matrix_tot_size) && (data_out_prefifo.valid & data_out_prefifo.ready) ? tot_cnt_q + 1 : tot_cnt_q; // "Smart shifting": this set of combinational blocks shifts data_in_unrolled // appropriately, depending on the configuration. @@ -292,6 +316,8 @@ module datamover_engine else $fatal("BANDWIDTH_ALIGNED (%0d) must not be greater than MAX_BANDWIDTH (%0d)", BANDWIDTH_ALIGNED, MAX_BANDWIDTH); assert ((BANDWIDTH_ALIGNED % WORD_WIDTH) == 0) else $fatal("BANDWIDTH_ALIGNED (%0d) must be a multiple of WORD_WIDTH (%0d)", BANDWIDTH_ALIGNED, WORD_WIDTH); + assert ((NB_ELEMENTS != 0) && ((NB_ELEMENTS & (NB_ELEMENTS - 1)) == 0)) + else $fatal("NB_ELEMENTS (%0d) = BANDWIDTH_ALIGNED (%0d) / ELEM_WIDTH (%0d) must be a power of two", NB_ELEMENTS, BANDWIDTH_ALIGNED, ELEM_WIDTH); assert (NUM_ELEM_WORD <= MAX_SHIFTING) // ToDo(cdurrer): obsolete? else $fatal("NUM_ELEM_WORD (%0d) must not be greater than MAX_SHIFTING (%0d)", NUM_ELEM_WORD, MAX_SHIFTING); end diff --git a/verif/python/generate_stimuli.py b/verif/python/generate_stimuli.py index 5b8d71a..a733da2 100644 --- a/verif/python/generate_stimuli.py +++ b/verif/python/generate_stimuli.py @@ -89,7 +89,6 @@ def write_element_to_memory(element, memory, elem_width, word_width, address): def write_matrix_to_memory(matrix, memory, elem_width, word_width, write_base_addr): """Write output matrix back to memory at specified base address.""" - elems_per_word = word_width // elem_width matrix_flat = sum(matrix, []) matrix_elems = len(matrix_flat) for i in range(matrix_elems): @@ -128,8 +127,6 @@ def write_data_header_file(output_dir, input_matrix, output_matrix, config_param """Write input and output matrices to a C header file with configuration parameters.""" os.makedirs(output_dir, exist_ok=True) # Ensure directory exists filepath = os.path.join(output_dir, "data.h") - size_m = len(input_matrix) - size_n = len(input_matrix[0]) if size_m > 0 else 0 input_flat = [elem for row in input_matrix for elem in row] output_flat = [elem for row in output_matrix for elem in row] @@ -181,34 +178,72 @@ def transpose(matrix, size_m, size_n, transp_mode): transposed[d0][(d1*transp_mode)+i] = matrix[d1][(d0*transp_mode)+i] return transposed -def cim_layout(matrix, size_m, size_n, cim_mode, cim_inner_dim, cim_outer_dim): + +def cim_layout(matrix, size_m, size_n, cim_mode, cim_inner_dim, cim_outer_dim, word_width_elems): if cim_mode == 0: # row-major -> A-Layout row_tile_size = cim_inner_dim - initial_size_m = size_m - initial_size_n = size_n elif cim_mode == 1: # row-major -> B-Layout row_tile_size = cim_outer_dim - initial_size_m = size_m - initial_size_n = size_n elif cim_mode == 2: # A-Layout -> row-major row_tile_size = cim_inner_dim - initial_size_m = size_n // row_tile_size - initial_size_n = size_m * row_tile_size elif cim_mode == 3: # B-Layout -> row-major row_tile_size = cim_outer_dim - initial_size_m = size_n // row_tile_size - initial_size_n = size_m * row_tile_size else: raise ValueError("[GM] cim_mode must be 0 (A-Layout), 1 (B-Layout), 2 (A-Layout -> row-major), or 3 (B-Layout -> row-major).") - print(f"initial_size_m: {initial_size_m}, initial_size_n: {initial_size_n}, row_tile_size: {row_tile_size}") - cim_matrix = [[0 for _ in range(initial_size_m * row_tile_size)] for _ in range(initial_size_n // row_tile_size)] - for d2 in range(initial_size_n // row_tile_size): - for d1 in range(initial_size_m): - cim_matrix[d2][(d1*row_tile_size):(d1*row_tile_size+row_tile_size)] = matrix[d1][d2*(row_tile_size):(d2*row_tile_size+row_tile_size)] + complete_n_tiles = size_n // row_tile_size + leftover_columns = size_n % row_tile_size + leftover_words = math.ceil(leftover_columns / word_width_elems) + out_words = (size_m * size_n) // word_width_elems + words_per_tile = row_tile_size // word_width_elems + # cim_matrix is a 2D list with a single row to represent the flattened layout + cim_matrix = [[0] * (size_m * size_n)] + + for d2 in range(complete_n_tiles): + for d1 in range(size_m): + chunk = matrix[d1][d2*(row_tile_size):(d2*row_tile_size+row_tile_size)] + for i in range(words_per_tile): + index = d2*size_m*row_tile_size + d1*row_tile_size + i*word_width_elems + cim_matrix[0][index : index + word_width_elems] = chunk[i*word_width_elems:(i*word_width_elems)+word_width_elems] + + if (leftover_columns > 0): + d2 = complete_n_tiles + for d1 in range(size_m): + chunk = matrix[d1][d2*(row_tile_size):(d2*row_tile_size+leftover_columns)] + for i in range(leftover_words): + index = d2*size_m*row_tile_size + d1*leftover_columns + i*leftover_columns + cim_matrix[0][index : index + leftover_columns] = chunk[i*leftover_columns:(i*leftover_columns)+leftover_columns] + return cim_matrix - # Reshape back to original dimensions for output - print(f"CIM Matrix Size: {len(cim_matrix)} x {len(cim_matrix[0])}") +def cim_layout_reverse(matrix, size_m, size_n, cim_mode, cim_inner_dim, cim_outer_dim, word_width_elems): # dimensions of original row-major layout are used + if cim_mode == 0: # row-major -> A-Layout + row_tile_size = cim_inner_dim + elif cim_mode == 1: # A-Layout -> row-major + row_tile_size = cim_inner_dim + elif cim_mode == 2: # row-major -> B-Layout + row_tile_size = cim_outer_dim + elif cim_mode == 3: # B-Layout -> row-major + row_tile_size = cim_outer_dim + else: + raise ValueError("[GM] cim_mode must be 0 (row-major -> A-Layout), 1 (A-Layout -> row-major), 2 (row-major -> B-Layout), or 3 (B-Layout -> row-major).") + + complete_n_tiles = size_n // row_tile_size + leftover_columns = size_n % row_tile_size + cim_matrix = [[0] * (size_m * size_n)] + flattened_input = [elem for row in matrix for elem in row] + for d1 in range(complete_n_tiles): + for d0 in range(size_m): + input_index = d1*size_m*row_tile_size + d0*row_tile_size + output_index = d0*size_n + d1*row_tile_size + cim_matrix[0][output_index : output_index + row_tile_size] = flattened_input[input_index : input_index + row_tile_size] + print(f"Processing tile {d1}, row {d0}: input index {input_index} to output index {output_index}") + + if (leftover_columns > 0): + for d0 in range (size_m): + input_index = complete_n_tiles*size_m*row_tile_size + d0*leftover_columns + output_index = complete_n_tiles*row_tile_size +d0*size_n + cim_matrix[0][output_index : output_index + leftover_columns] = flattened_input[input_index : input_index + leftover_columns] + print(f"Processing leftover columns for row {d0}: input index {input_index} to output index {output_index}") return cim_matrix def unfold(tensor, patch_size): @@ -253,7 +288,6 @@ def main(): BANDWIDTH_ALIGNED = args.bandwidth_bits - (args.misaligned_accesses * (args.elem_width * args.num_elem_word)) MEMORY_SIZE = args.mem_size # Set global memory size BANDWIDTH_ELEMS = BANDWIDTH_ALIGNED // args.elem_width - WORD_SIZE_BITS = args.num_elem_word * args.elem_width # Set global word size in bits ELEM_WIDTH = args.elem_width WORD_WIDTH = args.num_elem_word * args.elem_width @@ -273,7 +307,7 @@ def main(): if args.num_elem_word & (args.num_elem_word - 1) != 0 or args.num_elem_word <= 0: raise ValueError("[GM] num_elem_word must be a power of two and greater than zero.") # bandwidth width must be a multiple of word size - if BANDWIDTH_ALIGNED % WORD_SIZE_BITS != 0: + if BANDWIDTH_ALIGNED % WORD_WIDTH != 0: raise ValueError("[GM] BANDWIDTH_ALIGNED must be a multiple of the word size (num_elem_word * elem_width).") # # bandwidth width must be a multiple of word size # if ((MATRIX_DIM_N * ELEM_WIDTH) < BANDWIDTH_ALIGNED): @@ -281,8 +315,8 @@ def main(): # read_tot_length must not exceed 12-bit register capacity (4096) # # BANDWIDTH_ALIGNED must be a power of 2 - # if ((BANDWIDTH_ALIGNED & (BANDWIDTH_ALIGNED - 1)) != 0) or (BANDWIDTH_ALIGNED < WORD_SIZE_BITS): - # raise ValueError(f"[GM] BANDWIDTH_ALIGNED ({BANDWIDTH_ALIGNED}) must be a power of 2 and greater than the WORD_SIZE ({WORD_SIZE_BITS}).") + # if ((BANDWIDTH_ALIGNED & (BANDWIDTH_ALIGNED - 1)) != 0) or (BANDWIDTH_ALIGNED < WORD_WIDTH): + # raise ValueError(f"[GM] BANDWIDTH_ALIGNED ({BANDWIDTH_ALIGNED}) must be a power of 2 and greater than the WORD_SIZE ({WORD_WIDTH}).") # if ((TOT_LENGTH >= 4096) & (args.datamover_mode != 0)): # raise ValueError("[GM] TOT_LENGTH (MxN / BW_ELEM) must be less than 4096 in transpose and CIM modes (12-bit register limit).") @@ -296,11 +330,11 @@ def main(): raise ValueError(f"[GM] Matrix width N ({MATRIX_DIM_N}) must be a multiple of transp_mode ({args.transp_mode}).") print(f"Memory Size: {MEMORY_SIZE} entries") - print(f"Word Size: {WORD_SIZE_BITS} bits") + print(f"Word Size: {WORD_WIDTH} bits") # memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) if RANDOM_STIMULI: - memory = generate_random_hex(MEMORY_SIZE, WORD_SIZE_BITS) # for testing + memory = generate_random_hex(MEMORY_SIZE, WORD_WIDTH) # for testing else: memory = generate_counting_hex(MEMORY_SIZE, ELEM_WIDTH, WORD_WIDTH) # for debugging @@ -318,9 +352,6 @@ def main(): # Print input matrix print("Input Matrix:") - # # for i, row in enumerate(input_matrix): - # # print(f"Row {i}: {row}") - # # print("\n") for i, row in enumerate(input_matrix): print(f"Row {i}: {[format(elem, 'X') for elem in row]}") @@ -329,47 +360,19 @@ def main(): elif args.datamover_mode == 1: # Transpose mode output_matrix = transpose(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, TRANSP_MODE) elif args.datamover_mode == 2: # CIM mode - output_matrix = cim_layout(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) - # elif args.datamover_mode == 2: # TEST CIM mode (TWICE: should be identical to input) - # intermediate_matrix = cim_layout(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) - # intermediate_size_m = len(intermediate_matrix) - # intermediate_size_n = len(intermediate_matrix[0]) - # # Print intermediate matrix - # print("\nIntermediate Matrix:") - # for i, row in enumerate(intermediate_matrix): - # print(f"Row {i}: {[format(elem, 'X') for elem in row]}") - # print("\n") - # output_matrix = cim_layout(intermediate_matrix, intermediate_size_m, intermediate_size_n, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + if args.cim_mode == 0: # row-major -> A-Layout + output_matrix = cim_layout(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, args.num_elem_word) + elif args.cim_mode == 1: # A-Layout -> row-major (use matrix dimenstions of original row-major layout) + output_matrix = cim_layout_reverse(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, args.num_elem_word) elif args.datamover_mode == 3: # CIM layout transpose mode (INPUT SIZES EXPECTED IN ORIGINAL (ROW-MAJOR) LAYOUT FORM!) - print("TRANSP_MODE = ", TRANSP_MODE) - converted_size_m = MATRIX_DIM_N // args.cim_inner_dim - converted_size_n = MATRIX_DIM_M * args.cim_inner_dim - # Reshape input_matrix to converted_size_m x converted_size_n - input_flat = [elem for row in input_matrix for elem in row] - reshaped_matrix = [ - [input_flat[i * converted_size_n + j] for j in range(converted_size_n)] - for i in range(converted_size_m)] - print("\nReshaped Matrix:") - for i, row in enumerate(reshaped_matrix): - print(f"Row {i}: {[format(elem, 'X') for elem in row]}") - print("\n") - - intermediate1_matrix = cim_layout(reshaped_matrix, converted_size_m, converted_size_n, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) - intermediate1_size_m = len(intermediate1_matrix) - intermediate1_size_n = len(intermediate1_matrix[0]) - - print("\nIntermediate1 Matrix (CIM layout -> row-major):") - for i, row in enumerate(intermediate1_matrix): - print(f"Row {i}: {[format(elem, 'X') for elem in row]}") - print("\n") - intermediate2_matrix = transpose(intermediate1_matrix, intermediate1_size_m, intermediate1_size_n, TRANSP_MODE) - intermediate2_size_m = len(intermediate2_matrix) - intermediate2_size_n = len(intermediate2_matrix[0]) - print("\nIntermediate2 Matrix (row major transpose):") - for i, row in enumerate(intermediate2_matrix): - print(f"Row {i}: {[format(elem, 'X') for elem in row]}") - print("\n") - output_matrix = cim_layout(intermediate2_matrix, intermediate2_size_m, intermediate2_size_n, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim) + input_matrix_chw = cim_layout_reverse(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, args.num_elem_word) + # Reshape input_matrix_chw to MATRIX_DIM_M x MATRIX_DIM_N + input_matrix_chw_flat = [elem for row in input_matrix_chw for elem in row] + input_matrix_chw = [input_matrix_chw_flat[i * MATRIX_DIM_N:(i + 1) * MATRIX_DIM_N] for i in range(MATRIX_DIM_M)] + transposed_chw = transpose(input_matrix_chw, MATRIX_DIM_M, MATRIX_DIM_N, TRANSP_MODE) + transposed_chw_flat = [elem for row in transposed_chw for elem in row] + transposed_chw = [transposed_chw_flat[i * MATRIX_DIM_N:(i + 1) * MATRIX_DIM_N] for i in range(MATRIX_DIM_M)] + output_matrix = cim_layout(transposed_chw, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, args.num_elem_word) else: raise ValueError("[GM] datamover_mode must be 0 (copy), 1 (transpose), or 2 (CIM).") @@ -380,8 +383,8 @@ def main(): print("\n") # Compare input and output matrix: equality check - if (output_matrix == input_matrix): - print("Output matrix equals input matrix.") + if ([val for row in output_matrix for val in row] == [val for row in input_matrix for val in row]): + print("Output matrix equals input matrix in memory (flattened).") memory = write_matrix_to_memory(output_matrix, memory, ELEM_WIDTH, WORD_WIDTH, WRITE_BASE_ADDR) diff --git a/verif/python/validate_config.py b/verif/python/validate_config.py index a5ae2ca..a87b5c7 100755 --- a/verif/python/validate_config.py +++ b/verif/python/validate_config.py @@ -48,15 +48,15 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, misaligned_a # CIM-specific validation if datamover_mode in [2, 3]: if cim_mode not in [0, 1]: - errors.append(f"CIM_MODE ({cim_mode}) must be 0 (row-major -> A-Layout) or 1 (row-major -> B-Layout)") + errors.append(f"CIM_MODE ({cim_mode}) must be 0 (row-major -> A-Layout) or 1 (A-Layout -> row-major) for CIM modes") if (cim_inner_dim % bandwidth_elems != 0) and (cim_mode == 0): errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) must be a multiple of bandwidth ({bandwidth_elems})") if (cim_outer_dim % bandwidth_elems != 0) and (cim_mode == 1): errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) must be a multiple of bandwidth ({bandwidth_elems})") - if (cim_inner_dim > matrix_dim_n) and (cim_mode == 0): - errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) cannot be greater than matrix width ({matrix_dim_n})") - if (cim_outer_dim > matrix_dim_m) and (cim_mode == 1): - errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) cannot be greater than matrix height ({matrix_dim_m})") + # if (cim_inner_dim > matrix_dim_n) and (cim_mode == 0): + # errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) cannot be greater than matrix width ({matrix_dim_n})") + # if (cim_outer_dim > matrix_dim_m) and (cim_mode == 1): + # errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) cannot be greater than matrix height ({matrix_dim_m})") # Memory requirements matrix_elements = matrix_dim_m * matrix_dim_n From a58e8a063bee315eee4bad238285a60233e09914 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Wed, 1 Apr 2026 10:28:13 +0200 Subject: [PATCH 28/29] [rtl, golden model] Implemented unfold and fold modes (MobileViT) --- Makefile | 6 +- config.mk | 29 ++- rtl/datamover_engine.sv | 196 ++++++++++++++----- rtl/datamover_package.sv | 12 +- rtl/datamover_streamer.sv | 2 + rtl/datamover_top.sv | 23 ++- verif/python/datamover_golden_model_numpy.py | 149 ++++++++++++-- verif/python/generate_stimuli.py | 20 +- verif/python/validate_config.py | 22 +-- verif/tb/tb_datamover_top_wrap.sv | 16 +- verif/tb/tb_package.sv | 4 +- 11 files changed, 361 insertions(+), 118 deletions(-) diff --git a/Makefile b/Makefile index 7a9c7df..c0c221f 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,9 @@ TESTBENCH_DEFINES += -DSTIM_TRANSP_MODE=${STIM_TRANSP_MODE} TESTBENCH_DEFINES += -DSTIM_MATRIX_DIM_M=${STIM_MATRIX_DIM_M} TESTBENCH_DEFINES += -DSTIM_MATRIX_DIM_N=${STIM_MATRIX_DIM_N} +TESTBENCH_DEFINES += -DSTIM_NUM_CHANNELS=${STIM_NUM_CHANNELS} +TESTBENCH_DEFINES += -DSTIM_TOTAL_ELEMENTS=${STIM_TOTAL_ELEMENTS} + TESTBENCH_DEFINES += -DBANDWIDTH=${BANDWIDTH} TESTBENCH_DEFINES += -DNUM_ELEM_WORD=${NUM_ELEM_WORD} TESTBENCH_DEFINES += -DELEM_WIDTH=${ELEM_WIDTH} @@ -303,7 +306,7 @@ validate-config: --word_width $(WORD_WIDTH) \ --elem_width $(ELEM_WIDTH) \ --memory_size $(MEMORY_SIZE) \ - --misaligned_accesses $(MISALIGNED_ACCESSES) \ + --num_channels $(MATRIX_DIM_C) \ --datamover_mode $(DATAMOVER_MODE) \ --transp_mode $(TRANSP_MODE) \ --cim_mode $(CIM_MODE) \ @@ -347,6 +350,7 @@ stimuli: clean-stimuli validate-config --cim_outer_dim $(CIM_OUTER_DIM) \ --matrix_dim_m $(MATRIX_DIM_M) \ --matrix_dim_n $(MATRIX_DIM_N) \ + --num_channels $(MATRIX_DIM_C) \ --output_dir "verif/python/generated" # Bender diff --git a/config.mk b/config.mk index 3dba9c4..52aabd7 100644 --- a/config.mk +++ b/config.mk @@ -17,33 +17,36 @@ BANDWIDTH ?= 512 # in bits, multiple of WORD_WIDTH (512) WORD_WIDTH ?= 64 # in bits, multiple of ELEM_WIDTH (64) ELEM_WIDTH ?= 8 # in bits (8) MEMORY_SIZE ?= 131072 # in words -MISALIGNED_ACCESSES ?= 0 +# MISALIGNED_ACCESSES ?= 0 -DATAMOVER_MODE ?= 1 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion, 3 = CIM data layout transpose +DATAMOVER_MODE ?= 1 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion, 3 = CIM data layout transpose, 4 = unfold (MobileViT), 5 = fold (MobileViT), other values: not accepted TRANSP_MODE ?= 1 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> A-Layout, 1: reverse (A-Layout -> row-major), ToDo (unnecessary for 64x64 CIM): 2: row-major -> B-Layout, 3: reverse (B-Layout -> row-major) CIM_INNER_DIM ?= 64 # Inner dimension of the CIM accelerator (in elements): 64 for 64x8 CIM macro CIM_OUTER_DIM ?= 64 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros # Input matrix dimensions (in elements) +NUM_CHANNELS ?= 1 # Number of channels (ToDo: currently, this should be set to 1 except for unfold mode) MATRIX_DIM_M ?= 64 # Matrix height in elements MATRIX_DIM_N ?= 64 # Matrix width in elements READ_BASE_ADDR = 0 # Derived constants from basic parameters -BANDWIDTH_REDUCTION := $(shell echo $$(($(MISALIGNED_ACCESSES) * $(WORD_WIDTH)))) # in bits -BANDWIDTH_ALIGNED := $(shell echo $$(($(BANDWIDTH) - $(BANDWIDTH_REDUCTION)))) # in bytes +# BANDWIDTH_REDUCTION := $(shell echo $$(($(MISALIGNED_ACCESSES) * $(WORD_WIDTH)))) # in bits +# BANDWIDTH_ALIGNED := $(shell echo $$(($(BANDWIDTH) - $(BANDWIDTH_REDUCTION)))) # in bits +BANDWIDTH_ALIGNED := $(BANDWIDTH) # in bits BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH_ALIGNED) / $(ELEM_WIDTH)))) # Number of elements per bandwidth NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of elements per word MATRIX_SIZE_TOT := $(shell echo $$(($(MATRIX_DIM_M) * $(MATRIX_DIM_N)))) # Total number of elements in the matrix -MATRIX_MISALIGNED := $(shell echo $$(($(MATRIX_SIZE_TOT) % $(BANDWIDTH_ELEMS)))) # 1 if matrix size is not multiple of bandwidth elements +TOTAL_ELEMENTS := $(shell echo $$(($(NUM_CHANNELS) * $(MATRIX_SIZE_TOT)))) # Total number of elements in all channels +MATRIX_MISALIGNED := $(shell echo $$(($(TOTAL_ELEMENTS) % $(BANDWIDTH_ELEMS)))) # 1 if matrix size is not multiple of bandwidth elements ifeq "$(strip $(MATRIX_MISALIGNED))" "0" # Matrix size aligned - TOTAL_ACCESSES := $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS)))) # Total number of memory accesses (words) for the matrix (floor division) + TOTAL_ACCESSES := $(shell echo $$(($(TOTAL_ELEMENTS) / $(BANDWIDTH_ELEMS)))) # Total number of memory accesses (words) for the matrix (floor division) else # Matrix size misaligned - TOTAL_ACCESSES := $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS) + 1))) # Total number of memory accesses (words) for the matrix (+1 for misaligned access) + TOTAL_ACCESSES := $(shell echo $$(($(TOTAL_ELEMENTS) / $(BANDWIDTH_ELEMS) + 1))) # Total number of memory accesses (words) for the matrix (+1 for misaligned access) endif -WRITE_BASE_ADDR = $(shell echo $$(($(READ_BASE_ADDR) + $(MATRIX_SIZE_TOT)))) # Element-addressed +WRITE_BASE_ADDR = $(shell echo $$(($(READ_BASE_ADDR) + $(TOTAL_ELEMENTS)))) # Element-addressed # Align matrix dimensions to bandwidth for transposition (fill elem_matrix) MATRIX_DIM_M_MOD := $(shell echo $$(( $(MATRIX_DIM_M) % $(BANDWIDTH_ELEMS) ))) @@ -81,6 +84,8 @@ STIM_TRANSP_MODE ?= 0 # STIM_TRANSP_LEN ?= 0 STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) +STIM_NUM_CHANNELS ?= 1 +STIM_TOTAL_ELEMENTS ?= $(TOTAL_ELEMENTS) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) STIM_WRITE_D0_LENGTH ?= $(STIM_READ_D0_LENGTH) @@ -119,6 +124,8 @@ STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 1 = 1 elem, 2 = 2 elem, 4 # STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) # Actual (non-aligned) matrix dimensions STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) +STIM_NUM_CHANNELS ?= 1 +STIM_TOTAL_ELEMENTS ?= $(TOTAL_ELEMENTS) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(BANDWIDTH_ELEMS) / $(TRANSP_MODE)))) # Transpose tile width corresponds to bandwidth @@ -157,6 +164,8 @@ STIM_TRANSP_MODE ?= 0 # STIM_TRANSP_LEN ?= 0 STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) +STIM_NUM_CHANNELS ?= 1 +STIM_TOTAL_ELEMENTS ?= $(TOTAL_ELEMENTS) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(MATRIX_DIM_M)))) @@ -190,7 +199,7 @@ STIM_READ_D2_STRIDE ?= $(shell echo $$(($(MATRIX_DIM_M) * $(CIM_INNER_DIM)))) STIM_READ_D3_LENGTH ?= 0 STIM_READ_D3_STRIDE ?= 0 STIM_READ_D4_STRIDE ?= 0 -STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(MATRIX_SIZE_TOT) / $(BANDWIDTH_ELEMS)))) +STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(TOTAL_ELEMENTS) / $(BANDWIDTH_ELEMS)))) STIM_READ_DIM_ENABLE ?= "4'b0011" STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] @@ -198,6 +207,8 @@ STIM_TRANSP_MODE ?= $(TRANSP_MODE) # transp_mode # STIM_TRANSP_LEN ?= 0 STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) +STIM_NUM_CHANNELS ?= 1 +STIM_TOTAL_ELEMENTS ?= $(TOTAL_ELEMENTS) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(BANDWIDTH_ELEMS) diff --git a/rtl/datamover_engine.sv b/rtl/datamover_engine.sv index 7b35f70..b547ecb 100644 --- a/rtl/datamover_engine.sv +++ b/rtl/datamover_engine.sv @@ -45,22 +45,15 @@ module datamover_engine ); // number of elements (in the full bandwidth, not a single bank word) - localparam NB_ELEMENTS = BANDWIDTH_ALIGNED / ELEM_WIDTH; + localparam int unsigned NB_ELEMENTS = BANDWIDTH_ALIGNED / ELEM_WIDTH; localparam int unsigned NB_ELEM_LOG2 = $clog2(NB_ELEMENTS); - logic [23:0] matrix_tot_size; - assign matrix_tot_size = ctrl_i.matrix_dim_m * ctrl_i.matrix_dim_n; // ToDo(cdurrer): additional MUL, problem? (Could also be configured in control register by the HAL) - logic [NB_ELEM_LOG2-1:0] remaining_elems; - assign remaining_elems = matrix_tot_size & (NB_ELEMENTS - 1); // modulo (NB_ELEMENTS: power of two) - logic [11:0] nof_accesses; - assign nof_accesses = (matrix_tot_size >> NB_ELEM_LOG2) + ((remaining_elems != 0) ? 1 : 0); - // Type def and internal signals typedef enum logic { WRITE, READ } datamover_engine_fsm_t; datamover_engine_fsm_t fsm_d, fsm_q; logic clear_elem_matrix; - logic [NB_ELEM_LOG2:0] cnt_q, cnt_d; - logic [15:0] tot_cnt_q, tot_cnt_d; // ToDo(cdurrer): bitwidth? + logic [NB_ELEM_LOG2-1:0] cnt_q, cnt_d; + logic [17:0] tot_cnt_q, tot_cnt_d; logic cnt_en; logic [NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] data_in_unrolled; logic data_in_valid; @@ -68,9 +61,16 @@ module datamover_engine logic [NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] data_out_unrolled; logic data_out_valid; logic data_out_ready; - logic [11:0] m_tiles, n_tile_cnt, m_elem_cnt,expanded_m_elems; // ToDo(cdurrer): review bitwidths + logic [23:0] matrix_tot_size; + logic [NB_ELEM_LOG2-1:0] remaining_elems; + logic [17:0] total_accesses_copy_mode, total_accesses; + logic [15:0] c_elem_cnt, expanded_c_elems; + logic [15:0] m_elem_cnt, expanded_m_elems; + logic [9:0] c_tiles, m_tiles, n_tiles, n_tile_cnt; logic [NB_ELEM_LOG2:0] leftover_rows, leftover_cols; - logic last_m_tile, last_n_tile; + logic last_c_tile, last_m_tile, last_n_tile; + logic write_to_buffer_done, read_from_buffer_done; + logic execution_done; // FSM: WRITE -> READ on input handshake at end of write, READ -> WRITE on output handshake at end of read always_comb @@ -78,12 +78,14 @@ module datamover_engine fsm_d = fsm_q; case (fsm_q) WRITE: begin - if ((cnt_q == ctrl_i.transp_len-ctrl_i.transp_stride) && (data_in_valid & data_in_ready)) begin + if (((cnt_q == ctrl_i.transp_len-ctrl_i.transp_stride)) && (data_in_valid & data_in_ready)) begin + // if ((write_to_buffer_done || (cnt_q == ctrl_i.transp_len-ctrl_i.transp_stride)) && (data_in_valid & data_in_ready)) begin fsm_d = READ; end end READ: begin - if ((cnt_q == ctrl_i.transp_len-ctrl_i.transp_stride) && (data_out_valid & data_out_ready)) begin + if (((cnt_q == ctrl_i.transp_len-ctrl_i.transp_stride)) && (data_out_valid & data_out_ready)) begin + // if ((read_from_buffer_done || (cnt_q == ctrl_i.transp_len-ctrl_i.transp_stride)) && (data_out_valid & data_out_ready)) begin fsm_d = WRITE; end end @@ -96,14 +98,14 @@ module datamover_engine always_ff @(posedge clk_i or negedge rst_ni) begin if (~rst_ni) begin fsm_q <= WRITE; - end else if (clear_i) begin + end else if (clear_i || execution_done) begin fsm_q <= WRITE; end else begin fsm_q <= fsm_d; end end - assign clear_elem_matrix = (fsm_q == READ && fsm_d == WRITE) ? 1'b1 : 1'b0; + assign clear_elem_matrix = (fsm_q == READ && fsm_d == WRITE); // internal interfaces and unrolling hwpe_stream_intf_stream #( @@ -154,57 +156,119 @@ module datamover_engine // Partial tile / leftover elements handling // Due to the streamer address generation, matrices need to be word-aligned in n-dimension for transposition localparam logic [NB_ELEMENTS-1:0] STRB_ONE = {{(NB_ELEMENTS-1){1'b0}}, 1'b1}; // Necessary to force the shifting operation to the correct bitwidth (default would be only 32b) + assign matrix_tot_size = ctrl_i.total_elements; // Pre-computed by HAL: num_channels * dim_m * dim_n + assign remaining_elems = matrix_tot_size & (NB_ELEMENTS - 1); // modulo (NB_ELEMENTS: power of two) - this signal is only used in copy mode + assign total_accesses_copy_mode = (matrix_tot_size >> NB_ELEM_LOG2) + ((remaining_elems != 0) ? 1 : 0); + + assign c_tiles = (ctrl_i.num_channels + NB_ELEMENTS - 1) >> NB_ELEM_LOG2; // ceil division // ToDo: combine c_tile signals with m_tiles according to mode (only one necessary at a time) assign m_tiles = (ctrl_i.matrix_dim_m + NB_ELEMENTS - 1) >> NB_ELEM_LOG2; // ceil division - // assign n_tiles = (ctrl_i.matrix_dim_n + NB_ELEMENTS - 1) / NB_ELEMENTS; - assign leftover_rows = ctrl_i.matrix_dim_m & (NB_ELEMENTS - 1); + assign n_tiles = (ctrl_i.matrix_dim_n + NB_ELEMENTS - 1) >> NB_ELEM_LOG2; // ceil division + assign total_accesses = (ctrl_i.datamover_mode == DATAMOVER_UNFOLD || ctrl_i.datamover_mode == DATAMOVER_FOLD) ? (c_tiles * ctrl_i.matrix_dim_m * n_tiles) << NB_ELEM_LOG2 : (m_tiles * n_tiles) << NB_ELEM_LOG2; // NB_ELEMENTS is a power of 2, so multiply by shifting; ToDo: remaining MUL overhead, could be pre-computed in HAL and configured in control register + assign leftover_rows = (ctrl_i.datamover_mode == DATAMOVER_UNFOLD || ctrl_i.datamover_mode == DATAMOVER_FOLD) ? ctrl_i.num_channels & (NB_ELEMENTS - 1) : ctrl_i.matrix_dim_m & (NB_ELEMENTS - 1); assign leftover_cols = ctrl_i.matrix_dim_n & (NB_ELEMENTS - 1); - assign expanded_m_elems = m_tiles * NB_ELEMENTS; - assign m_elem_cnt = (expanded_m_elems == 0) ? '0 : (tot_cnt_q % expanded_m_elems); - assign n_tile_cnt = (expanded_m_elems == 0) ? '0 : (tot_cnt_q / expanded_m_elems); + assign expanded_c_elems = c_tiles << NB_ELEM_LOG2; + assign expanded_m_elems = m_tiles << NB_ELEM_LOG2; + assign c_elem_cnt = (expanded_c_elems == 0) ? '0 : (tot_cnt_q % expanded_c_elems); // ToDo: restructure without modulo (pre-compute in HAL?) + assign m_elem_cnt = (expanded_m_elems == 0) ? '0 : (tot_cnt_q % expanded_m_elems); // ToDo: restructure without modulo (pre-compute in HAL?) + assign n_tile_cnt = (ctrl_i.datamover_mode == DATAMOVER_UNFOLD || ctrl_i.datamover_mode == DATAMOVER_FOLD) ? (tot_cnt_q / expanded_c_elems) : (tot_cnt_q / expanded_m_elems); // ToDo: restructure without division (pre-compute in HAL?) + assign last_c_tile = ((c_elem_cnt >> NB_ELEM_LOG2) >= (ctrl_i.num_channels >> NB_ELEM_LOG2)); assign last_m_tile = ((m_elem_cnt >> NB_ELEM_LOG2) >= (ctrl_i.matrix_dim_m >> NB_ELEM_LOG2)); assign last_n_tile = (n_tile_cnt >= (ctrl_i.matrix_dim_n >> NB_ELEM_LOG2)); always_comb begin - if(ctrl_i.datamover_mode == 0) begin // Copy mode - data_out_prefifo.strb = ((tot_cnt_q >= nof_accesses-1) && (remaining_elems != 0)) ? ((STRB_ONE << remaining_elems) - 1) : '1; - end else if(ctrl_i.datamover_mode == 1) begin // Transpose mode - if((last_m_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin - if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin + write_to_buffer_done = 1'b0; + read_from_buffer_done = 1'b0; + if(matrix_tot_size != 0) begin + if(ctrl_i.datamover_mode == DATAMOVER_COPY) begin // Copy mode + data_out_prefifo.strb = ((tot_cnt_q >= total_accesses_copy_mode-1) && (remaining_elems != 0)) ? ((STRB_ONE << remaining_elems) - 1) : '1; + // write_to_buffer_done = (tot_cnt_q >= (matrix_tot_size >> NB_ELEM_LOG2) - 1); + end else if(ctrl_i.datamover_mode == DATAMOVER_TRANSPOSE) begin // Transpose mode + if((last_m_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin + if(cnt_q >= leftover_rows) begin + write_to_buffer_done = (fsm_q == WRITE) ? 1'b1 : 1'b0; + read_from_buffer_done = (fsm_q == READ) ? 1'b1 : 1'b0; + end + if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin + data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); + end else begin + data_out_prefifo.strb = '0; + end + end else if(last_m_tile && leftover_rows != 0) begin + if(cnt_q >= leftover_rows) begin + write_to_buffer_done = (fsm_q == WRITE) ? 1'b1 : 1'b0; + // read_from_buffer_done = (fsm_q == READ) ? 1'b1 : 1'b0; + end data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); + end else if(last_n_tile && leftover_cols != 0) begin + if(cnt_q >= leftover_rows) begin + // write_to_buffer_done = (fsm_q == WRITE) ? 1'b1 : 1'b0; + read_from_buffer_done = (fsm_q == READ) ? 1'b1 : 1'b0; + end + if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin + data_out_prefifo.strb = '1; + end else begin + data_out_prefifo.strb = '0; + end end else begin - data_out_prefifo.strb = '0; - end - end else if(last_m_tile && leftover_rows != 0) begin - data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); - end else if(last_n_tile && leftover_cols != 0) begin - if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin data_out_prefifo.strb = '1; - end else begin - data_out_prefifo.strb = '0; end - end else begin - data_out_prefifo.strb = '1; - end - end else if(ctrl_i.datamover_mode == 2) begin // CIM layout conversion mode - if((last_m_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin - if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + end else if(ctrl_i.datamover_mode == DATAMOVER_CIM_CONVERSION) begin // CIM layout conversion mode + if((last_m_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin + if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + data_out_prefifo.strb = ((STRB_ONE << leftover_cols) - 1); + end else begin + data_out_prefifo.strb = '0; + end + end else if(last_m_tile && leftover_rows != 0) begin + if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + data_out_prefifo.strb = '1; + end else begin + data_out_prefifo.strb = '0; + end + end else if(last_n_tile && leftover_cols != 0) begin data_out_prefifo.strb = ((STRB_ONE << leftover_cols) - 1); end else begin - data_out_prefifo.strb = '0; + data_out_prefifo.strb = '1; end - end else if(last_m_tile && leftover_rows != 0) begin - if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + end else if(ctrl_i.datamover_mode == DATAMOVER_UNFOLD) begin // Unfold mode + if((last_c_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin + if((c_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin + data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); + end else begin + data_out_prefifo.strb = '0; + end + end else if(last_c_tile && leftover_rows != 0) begin + data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); + end else if(last_n_tile && leftover_cols != 0) begin + if((c_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin + data_out_prefifo.strb = '1; + end else begin + data_out_prefifo.strb = '0; + end + end else begin data_out_prefifo.strb = '1; + end + end else if(ctrl_i.datamover_mode == DATAMOVER_FOLD) begin // Fold mode (inverse of unfold: leftover_rows <-> leftover_cols roles swapped) + if((last_c_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin + if((c_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + data_out_prefifo.strb = ((STRB_ONE << leftover_cols) - 1); + end else begin + data_out_prefifo.strb = '0; + end + end else if(last_n_tile && leftover_cols != 0) begin + data_out_prefifo.strb = ((STRB_ONE << leftover_cols) - 1); + end else if(last_c_tile && leftover_rows != 0) begin + if((c_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + data_out_prefifo.strb = '1; + end else begin + data_out_prefifo.strb = '0; + end end else begin - data_out_prefifo.strb = '0; + data_out_prefifo.strb = '1; end - end else if(last_n_tile && leftover_cols != 0) begin - data_out_prefifo.strb = ((STRB_ONE << leftover_cols) - 1); end else begin - data_out_prefifo.strb = '1; + data_out_prefifo.strb = '1; // ToDo: Could be set to 0 if all cases are handled properly end - end else begin - data_out_prefifo.strb = '1; // ToDo: Could be set to 0 if all cases are handled properly end end @@ -223,16 +287,23 @@ module datamover_engine cnt_q <= '0; tot_cnt_q <= '0; end + else if(execution_done) begin + cnt_q <= '0; + tot_cnt_q <= '0; + end else if(cnt_en) begin cnt_q <= cnt_d; tot_cnt_q <= tot_cnt_d; end end - assign cnt_d = cnt_q < ctrl_i.transp_len-ctrl_i.transp_stride ? cnt_q+ctrl_i.transp_stride : '0; + assign cnt_d = (cnt_q < (ctrl_i.transp_len-ctrl_i.transp_stride)) ? cnt_q+ctrl_i.transp_stride : '0; + // assign cnt_d = ((write_to_buffer_done == 1'b0) || (cnt_q < (ctrl_i.transp_len-ctrl_i.transp_stride))) ? cnt_q+ctrl_i.transp_stride : '0; assign cnt_en = fsm_q == WRITE ? data_in_valid & data_in_ready : data_out_valid & data_out_ready; + assign execution_done = (ctrl_i.datamover_mode == DATAMOVER_COPY) ? (total_accesses_copy_mode != 0) && (data_out_prefifo.valid & data_out_prefifo.ready) && (tot_cnt_q >= total_accesses_copy_mode - 1) : + (total_accesses != 0) && (data_out_prefifo.valid & data_out_prefifo.ready) && (tot_cnt_q >= total_accesses - 1); // count total number of write accesses - assign tot_cnt_d = (tot_cnt_q < matrix_tot_size) && (data_out_prefifo.valid & data_out_prefifo.ready) ? tot_cnt_q + 1 : tot_cnt_q; + assign tot_cnt_d = (data_out_prefifo.valid & data_out_prefifo.ready) ? tot_cnt_q + 1 : tot_cnt_q; // "Smart shifting": this set of combinational blocks shifts data_in_unrolled // appropriately, depending on the configuration. @@ -263,6 +334,9 @@ module datamover_engine // Buffering matrix: this 2D array of word elements (e.g., bytes // when ELEM_WIDTH = 8) is used to buffer values to transpose. logic [NB_ELEMENTS-1:0][NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] elem_matrix_q; + logic clear_int; + assign clear_int = clear_i | clear_elem_matrix; + for(genvar ii=0; ii= ctrl_i.transp_stride + ) else $error("transp_stride (%0d) must not exceed transp_len (%0d) — unsigned subtraction underflow", + ctrl_i.transp_stride, ctrl_i.transp_len); + + assert property (@(posedge clk_i) disable iff (!rst_ni) + ctrl_i.transp_len <= NB_ELEMENTS + ) else $error("transp_len (%0d) exceeds NB_ELEMENTS (%0d) — cnt_q will never match FSM transition condition", + ctrl_i.transp_len, NB_ELEMENTS); + + assert property (@(posedge clk_i) disable iff (!rst_ni) + total_accesses >= (m_tiles * n_tiles * NB_ELEMENTS) + ) else $error("total_accesses overflow detected: m_tiles=%0d, n_tiles=%0d, NB_ELEMENTS=%0d", + m_tiles, n_tiles, NB_ELEMENTS); `endif `endif `endif diff --git a/rtl/datamover_package.sv b/rtl/datamover_package.sv index bbda557..fbc6c8b 100644 --- a/rtl/datamover_package.sv +++ b/rtl/datamover_package.sv @@ -32,13 +32,16 @@ package datamover_package; parameter int unsigned MAX_BANDWIDTH = 512; // support maximum 512bits of bandwidth typedef enum logic[1:0] { TRANSP_NONE, TRANSP_1ELEM, TRANSP_2ELEM, TRANSP_4ELEM } transp_mode_e; + typedef enum logic[4:0] { DATAMOVER_COPY, DATAMOVER_TRANSPOSE, DATAMOVER_CIM_CONVERSION, DATAMOVER_CIM_TRANSPOSE, DATAMOVER_UNFOLD, DATAMOVER_FOLD } datamover_mode_e; typedef struct packed { transp_mode_e transp_mode; logic [$clog2(MAX_BANDWIDTH/8):0] transp_len; - logic [2:0] transp_stride; // 1, 2, or 4 - logic [4:0] datamover_mode; // 0: copy, 1: tranpose, 2: CIM layout conversion + logic [2:0] transp_stride; // 1, 2, or 4 elements + datamover_mode_e datamover_mode; // 0: copy, 1: tranpose, 2: CIM layout conversion logic [11:0] matrix_dim_m; logic [11:0] matrix_dim_n; + logic [20:0] total_elements; // num_channels * dim_m * dim_n (pre-computed by HAL) + logic [10:0] num_channels; // number of channels (for unfolding/folding) } ctrl_engine_t; parameter int unsigned HWPE_REGISTER_OFFS = 32'h00; // Standard HWPE register offset @@ -70,8 +73,9 @@ package datamover_package; parameter int unsigned DATAMOVER_REG_OUT_D2 = 32'h24; // [31:16] out_d2_stride; [15:0] out_d2_len parameter int unsigned DATAMOVER_REG_OUT_D3 = 32'h28; // [31:16] out_d3_stride; [15:0] out_d3_len parameter int unsigned DATAMOVER_REG_IN_OUT_D4_STRIDE = 32'h2C; // [31:16] out_d4_stride; [15:0] in_d4_stride (d4_len unnecessary due to tot_len) - parameter int unsigned DATAMOVER_REG_DIM_ENABLE = 32'h30; // [31:8] unused;[7:4] write_dim_en; [3:0] read_dim_en -> one-hot encoding (LSB->d1), d0 is always enabled - parameter int unsigned DATAMOVER_REG_CTRL_ENGINE = 32'h34; // [31:27] datamover_mode (0: copy, 1: transpose, 2: CIM layout conversion); [26:15] matrix_dim_n; [14:3] matrix_dim_m [2:0] transp_mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) + parameter int unsigned DATAMOVER_REG_MATRIX_DIM = 32'h30; // [31:16] matrix_dim_n; [15:0] matrix_dim_m + parameter int unsigned DATAMOVER_REG_CHANNELS = 32'h34; // [31:11] total_elements = num_channels * dim_m * dim_n (pre-compute to save HW resources); [10:0] num_channels (for unfolding/folding) + parameter int unsigned DATAMOVER_REG_CTRL_ENGINE = 32'h38; // [15:12] write_dim_en; [11:8] read_dim_en; [7:3] datamover_mode; [2:0] transp_mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) // Note: increase N_IO_REGS in datamover_top.sv when adding new registers here! diff --git a/rtl/datamover_streamer.sv b/rtl/datamover_streamer.sv index 75a870c..c3c62f3 100644 --- a/rtl/datamover_streamer.sv +++ b/rtl/datamover_streamer.sv @@ -109,6 +109,7 @@ module datamover_streamer .ELEMENT_WIDTH ( ELEM_WIDTH ), // e.g., 8 bits per element .ELEMENTS_PER_BANK ( NUM_ELEM_WORD ), // number of elements in one memory bank word .MISALIGNED_ACCESSES ( MISALIGNED_ACCESSES ), + .DIM_ENABLE_1H ( 4'b1111 ), .`HCI_SIZE_PARAM(tcdm) ( `HCI_SIZE_PARAM(tcdm) ) ) i_source ( .clk_i ( clk_i ), @@ -129,6 +130,7 @@ module datamover_streamer .ELEMENT_WIDTH ( ELEM_WIDTH ), // e.g., 8 bits per element .ELEMENTS_PER_BANK ( NUM_ELEM_WORD ), // number of elements in one memory bank word .MISALIGNED_ACCESSES ( MISALIGNED_ACCESSES ), + .DIM_ENABLE_1H ( 4'b1111 ), .`HCI_SIZE_PARAM(tcdm) ( `HCI_SIZE_PARAM(tcdm) ) ) i_sink ( .clk_i ( clk_i ), diff --git a/rtl/datamover_top.sv b/rtl/datamover_top.sv index 3d9fd39..2f024dc 100644 --- a/rtl/datamover_top.sv +++ b/rtl/datamover_top.sv @@ -23,7 +23,7 @@ module datamover_top import hci_package::*; import datamover_package::*; #( - parameter int unsigned ID = 10, // control slave peripheral ID width + parameter int unsigned ID = 4, // control slave peripheral ID width - ToDo(cdurrer): necessary? parameter int unsigned BANDWIDTH = 288, // total bandwidth of HWPE to TCDM (in bits) parameter int unsigned NUM_ELEM_WORD = 4, // number of elements in a memory bank word parameter int unsigned ELEM_WIDTH = 8, // element width (in bits) @@ -33,7 +33,8 @@ module datamover_top parameter hci_size_parameter_t `HCI_SIZE_PARAM(tcdm) = '0, // Dependent parameters: do not modify! localparam int unsigned WORD_WIDTH = NUM_ELEM_WORD * ELEM_WIDTH, // should correspond to bank width - localparam int unsigned NUM_WORDS = BANDWIDTH / WORD_WIDTH // TCDM interface width in number of words + localparam int unsigned NUM_WORDS = BANDWIDTH / WORD_WIDTH, // TCDM interface width in number of words + localparam int unsigned N_IO_REGS = 15 // number of configuration registers exposed by the control slave, adapt here if number of configuration registers changes ) ( // global signals input logic clk_i, @@ -148,7 +149,7 @@ module datamover_top .REGFILE_SCM ( 0 ), .N_CORES ( N_CORES ), .N_CONTEXT ( N_CONTEXT ), - .N_IO_REGS ( 14 ), + .N_IO_REGS ( N_IO_REGS ), .N_GENERIC_REGS ( 8 ), .ID_WIDTH ( ID ) ) i_slave ( @@ -213,12 +214,12 @@ module datamover_top always_comb begin streamer_ctrl_cfg = '0; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.dim_enable_1h = reg_file.hwpe_params[DATAMOVER_REG_DIM_ENABLE >> 2][3:0]; // Enabled dimensions (d0 is always enabled) - streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.dim_enable_1h = reg_file.hwpe_params[DATAMOVER_REG_DIM_ENABLE >> 2][7:4]; // Enabled dimensions (d0 is always enabled) + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.dim_enable_1h = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][11:8]; // Enabled dimensions (d0 is always enabled) + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.dim_enable_1h = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][15:12]; // Enabled dimensions (d0 is always enabled) streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.base_addr = reg_file.hwpe_params[DATAMOVER_REG_IN_PTR >> 2]; streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.base_addr = reg_file.hwpe_params[DATAMOVER_REG_OUT_PTR >> 2]; - streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.tot_len = reg_file.hwpe_params[DATAMOVER_REG_TOT_LEN >> 2][11:0]; - streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.tot_len = reg_file.hwpe_params[DATAMOVER_REG_TOT_LEN >> 2][11:0]; + streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.tot_len = reg_file.hwpe_params[DATAMOVER_REG_TOT_LEN >> 2][31:0]; + streamer_ctrl_cfg.data_out_sink_ctrl.addressgen_ctrl.tot_len = reg_file.hwpe_params[DATAMOVER_REG_TOT_LEN >> 2][31:0]; streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d0_len = reg_file.hwpe_params[DATAMOVER_REG_IN_D0 >> 2][15:0]; streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d0_stride = reg_file.hwpe_params[DATAMOVER_REG_IN_D0 >> 2][31:16]; streamer_ctrl_cfg.data_in_source_ctrl.addressgen_ctrl.d1_len = reg_file.hwpe_params[DATAMOVER_REG_IN_D1 >> 2][15:0]; @@ -249,9 +250,11 @@ module datamover_top engine_ctrl.transp_stride = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b000 ? 1 : reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b001 ? 1 : reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b010 ? 2 : 4; - engine_ctrl.datamover_mode = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][31:27]; - engine_ctrl.matrix_dim_m = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][14:3]; - engine_ctrl.matrix_dim_n = reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][26:15]; + engine_ctrl.datamover_mode = datamover_mode_e'(reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][7:3]); + engine_ctrl.matrix_dim_m = reg_file.hwpe_params[DATAMOVER_REG_MATRIX_DIM >> 2][15:0]; + engine_ctrl.matrix_dim_n = reg_file.hwpe_params[DATAMOVER_REG_MATRIX_DIM >> 2][31:16]; + engine_ctrl.num_channels = reg_file.hwpe_params[DATAMOVER_REG_CHANNELS >> 2][10:0]; + engine_ctrl.total_elements = reg_file.hwpe_params[DATAMOVER_REG_CHANNELS >> 2][31:11]; engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; // ToDo(cdurrer): What exactly did transp_len do? Use it to handle leftovers? // if(reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16] == '0) begin // no leftover // engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; // ToDo(cdurrer): why? diff --git a/verif/python/datamover_golden_model_numpy.py b/verif/python/datamover_golden_model_numpy.py index fe3a26b..b9f5d61 100644 --- a/verif/python/datamover_golden_model_numpy.py +++ b/verif/python/datamover_golden_model_numpy.py @@ -1,18 +1,86 @@ -import random -import argparse import os import math -from unittest import case import numpy as np -RANDOM_STIMULI = False # If False, stimuli are generated in a counting fashion +RANDOM_STIMULI = True # If False, stimuli are generated in a counting fashion for easier debugging -CIM_MODE = 4 # 0: copy, 1: transpose, 2: CIM data layout conversion, 3: CIM layout transpose, 4: unfold +CHANNELS = 32 +HEIGHT = 32 +WIDTH = 32 +PATCH_SIZE = 4 # MobileViT: 2x2 = 4 (square) (not tested for other patch sizes) + +# data.h generation parameters +BANDWIDTH = 512 +WORD_WIDTH = 64 +ELEM_WIDTH = 8 +MEMORY_SIZE = 131072 +DATAMOVER_MODE = 5 # 0: copy, 1: transpose, 2: CIM data layout conversion, 3: CIM layout transpose, 4: unfold (MobileViT), 5: fold (MobileViT), other values: not accepted +TRANSP_MODE = 1 +CIM_MODE = 0 +CIM_INNER_DIM = 64 +CIM_OUTER_DIM = 64 +SIZE_C = CHANNELS +SIZE_M = HEIGHT +SIZE_N = WIDTH + +def data_header_format(data, elements_per_line=16): + lines = [] + for i in range(0, len(data), elements_per_line): + line_elements = data[i:i + elements_per_line] + formatted_elements = [f"0x{elem:02x}" for elem in line_elements] + if i + elements_per_line < len(data): + line = " " + ", ".join(formatted_elements) + "," + else: + line = " " + ", ".join(formatted_elements) + lines.append(line) + return lines + + +def write_data_header_file(output_dir, input_matrix, output_matrix, config_params): + os.makedirs(output_dir, exist_ok=True) + filepath = os.path.join(output_dir, "data.h") + + input_flat = np.asarray(input_matrix, dtype=np.uint8).reshape(-1).tolist() + output_flat = np.asarray(output_matrix, dtype=np.uint8).reshape(-1).tolist() + + data_h_string = [ + "#pragma once", + "", + "#include ", + "", + "// Configuration Parameters", + f"#define BANDWIDTH {config_params['bandwidth']}", + f"#define WORD_WIDTH {config_params['word_width']}", + f"#define ELEM_WIDTH {config_params['elem_width']}", + f"#define MEMORY_SIZE {config_params['memory_size']}", + f"#define DATAMOVER_MODE {config_params['datamover_mode']}", + f"#define TRANSP_MODE {config_params['transp_mode']}", + f"#define CIM_MODE {config_params['cim_mode']}", + f"#define CIM_INNER_DIM {config_params['cim_inner_dim']}", + f"#define CIM_OUTER_DIM {config_params['cim_outer_dim']}", + f"#define SIZE_C {config_params['matrix_dim_c']}", + f"#define SIZE_M {config_params['matrix_dim_m']}", + f"#define SIZE_N {config_params['matrix_dim_n']}", + "", + "PI_L1 uint8_t golden_in [SIZE_C*SIZE_M*SIZE_N] = {", # PI_L1 only for GVSoC (siracusa) + ] + data_h_string.extend(data_header_format(input_flat)) + data_h_string.extend([ + "};", + "", + "PI_L1 uint8_t golden_out [SIZE_C*SIZE_M*SIZE_N] = {", # PI_L1 only for GVSoC (siracusa) + ]) + data_h_string.extend(data_header_format(output_flat)) + data_h_string.extend([ + "};", + "", + ]) + + with open(filepath, "w", encoding="utf-8") as file: + file.write("\n".join(data_h_string)) + + return filepath -CHANNELS = 2 -HEIGHT = 4 -WIDTH = 4 -PATCH_SIZE = 4 def unfold(tensor, patch_size): # Input tensor shape: (CHANNELS, HEIGHT, WIDTH) @@ -33,7 +101,32 @@ def unfold(tensor, patch_size): tensor_unfolded[p, n, :] = tensor[:, h_idx, w_idx] return tensor_unfolded +def fold(tensor, patch_size, num_channels, height, width): # Parameters: output tensor dimensions (CHW) + # Input tensor shape: (PATCH_SIZE, NUM_PATCHES, CHANNELS) -- calculated from output dimensions + # Output tensor shape: (CHANNELS, HEIGHT, WIDTH) -- num_channels, height, width are the folded dimensions + patch_sidelength = int(math.sqrt(patch_size)) + # size_n = (height * width) // patch_size + assert (height % patch_sidelength == 0) and (width % patch_sidelength == 0), "Height and Width must be divisible by patch sidelength" + num_patches_h = height // patch_sidelength + num_patches_w = width // patch_sidelength + tensor_folded = np.zeros((num_channels, height, width), dtype=tensor.dtype) + for p in range(patch_size): + for h in range(num_patches_h): + for w in range(num_patches_w): + n = h * num_patches_w + w + h_idx = h * patch_sidelength + (p // patch_sidelength) + w_idx = w * patch_sidelength + (p % patch_sidelength) + tensor_folded[:, h_idx, w_idx] = tensor[p, n, :] + return tensor_folded + def main(): + # Check configuration + required_elements = 3 * CHANNELS * HEIGHT * WIDTH + assert required_elements <= MEMORY_SIZE, ( + f"MEMORY_SIZE ({MEMORY_SIZE}) is too small: requires at least " + f"3*CHANNELS*HEIGHT*WIDTH = {required_elements} elements" + ) + # Create a tensor of size (CHANNELS, HEIGHT, WIDTH) with random or counting values if RANDOM_STIMULI: input_tensor = np.random.randint(0, 256, (CHANNELS, HEIGHT, WIDTH), dtype=np.uint8) @@ -41,21 +134,47 @@ def main(): input_tensor = np.arange(CHANNELS * HEIGHT * WIDTH, dtype=np.uint8).reshape((CHANNELS, HEIGHT, WIDTH)) print("Input Tensor:") print(input_tensor) - if CIM_MODE==0: + if DATAMOVER_MODE==0: output_tensor = input_tensor.copy() - elif CIM_MODE==1: + elif DATAMOVER_MODE==1: output_tensor = np.transpose(input_tensor, (0, 2, 1)) - # elif CIM_MODE==2: + # elif DATAMOVER_MODE==2: # output_tensor = cim_layout(input_tensor, ) - # elif CIM_MODE==3: + # elif DATAMOVER_MODE==3: # output_tensor = cim_transpose(input_tensor, ) - elif CIM_MODE==4: + elif DATAMOVER_MODE==4: output_tensor = unfold(input_tensor, PATCH_SIZE) + elif DATAMOVER_MODE==5: + # For fold mode, generate the input as an unfolded tensor by first unfolding a counting tensor + # base_tensor = np.arange(CHANNELS * HEIGHT * WIDTH, dtype=np.uint8).reshape((CHANNELS, HEIGHT, WIDTH)) + unfolded_tensor = unfold(input_tensor, PATCH_SIZE) + input_tensor = unfolded_tensor.copy() # Use the unfolded tensor as input for fold mode + print("\nUnfolded Tensor (input for fold mode):") + print(unfolded_tensor) + output_tensor = fold(unfolded_tensor, PATCH_SIZE, CHANNELS, HEIGHT, WIDTH) else: - raise ValueError(f"Unsupported CIM_MODE: {CIM_MODE}") + raise ValueError(f"Unsupported DATAMOVER_MODE: {DATAMOVER_MODE}") print("\nOutput Tensor:") print(output_tensor) + output_dir = os.path.join(os.path.dirname(__file__), "generated") + config_params = { + "bandwidth": BANDWIDTH, + "word_width": WORD_WIDTH, + "elem_width": ELEM_WIDTH, + "memory_size": MEMORY_SIZE, + "datamover_mode": DATAMOVER_MODE, + "transp_mode": TRANSP_MODE, + "cim_mode": CIM_MODE, + "cim_inner_dim": CIM_INNER_DIM, + "cim_outer_dim": CIM_OUTER_DIM, + "matrix_dim_c": SIZE_C, + "matrix_dim_m": SIZE_M, + "matrix_dim_n": SIZE_N, + } + header_file = write_data_header_file(output_dir, input_tensor, output_tensor, config_params) + print(f"\nWrote golden header to: {header_file}") + if __name__ == "__main__": main() diff --git a/verif/python/generate_stimuli.py b/verif/python/generate_stimuli.py index a733da2..9b66ac5 100644 --- a/verif/python/generate_stimuli.py +++ b/verif/python/generate_stimuli.py @@ -143,22 +143,23 @@ def write_data_header_file(output_dir, input_matrix, output_matrix, config_param f"#define WORD_WIDTH {config_params['word_width']}", f"#define ELEM_WIDTH {config_params['elem_width']}", f"#define MEMORY_SIZE {config_params['memory_size']}", - f"#define MISALIGNED_ACCESSES {config_params['misaligned_accesses']}", + # f"#define MISALIGNED_ACCESSES {config_params['misaligned_accesses']}", f"#define DATAMOVER_MODE {config_params['datamover_mode']}", f"#define TRANSP_MODE {config_params['transp_mode']}", f"#define CIM_MODE {config_params['cim_mode']}", f"#define CIM_INNER_DIM {config_params['cim_inner_dim']}", f"#define CIM_OUTER_DIM {config_params['cim_outer_dim']}", + f"#define SIZE_C {config_params['num_channels']}", f"#define SIZE_M {config_params['matrix_dim_m']}", f"#define SIZE_N {config_params['matrix_dim_n']}", "", - "uint8_t golden_in [SIZE_M*SIZE_N] = {", + "uint8_t golden_in [SIZE_C*SIZE_M*SIZE_N] = {", ] data_h_string.extend(data_header_format(input_flat)) data_h_string.extend([ "};", "", - "uint8_t golden_out [SIZE_M*SIZE_N] = {", + "uint8_t golden_out [SIZE_C*SIZE_M*SIZE_N] = {", ]) data_h_string.extend(data_header_format(output_flat)) data_h_string.extend([ @@ -273,19 +274,20 @@ def main(): parser.add_argument("--bandwidth_bits", type=int, default=4, help="Number of bits per transaction") parser.add_argument("--num_elem_word", type=int, default=4, help="Number of elements in a memory bank word") parser.add_argument("--elem_width", type=int, default=8, help="Width of each element (in bits)") - parser.add_argument("--misaligned_accesses", type=int, default=0, help="Enable misaligned accesses (0=disabled, 1=enabled)") + # parser.add_argument("--misaligned_accesses", type=int, default=0, help="Enable misaligned accesses (0=disabled, 1=enabled)") parser.add_argument("--datamover_mode", type=int, default=0, help="Datamover mode (0=normal, 1=CIM)") parser.add_argument("--transp_mode", type=int, default=0, help="Transposition mode (3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem)") parser.add_argument("--cim_mode", type=int, default=0, help="CIM mode (0=normal, 1=CIM)") parser.add_argument("--cim_inner_dim", type=int, default=4, help="CIM inner dimension") parser.add_argument("--cim_outer_dim", type=int, default=4, help="CIM outer dimension") + parser.add_argument("--num_channels", type=int, default=1, help="Number of channels") parser.add_argument("--matrix_dim_m", type=int, default=64, help="Matrix height in elements") parser.add_argument("--matrix_dim_n", type=int, default=64, help="Matrix width in elements") parser.add_argument("--output_dir", type=str, default="output", help="Directory for storing output files") args = parser.parse_args() - BANDWIDTH_ALIGNED = args.bandwidth_bits - (args.misaligned_accesses * (args.elem_width * args.num_elem_word)) + BANDWIDTH_ALIGNED = args.bandwidth_bits #- (args.misaligned_accesses * (args.elem_width * args.num_elem_word)) MEMORY_SIZE = args.mem_size # Set global memory size BANDWIDTH_ELEMS = BANDWIDTH_ALIGNED // args.elem_width @@ -294,15 +296,16 @@ def main(): READ_BASE_ADDR = args.read_base_addr WRITE_BASE_ADDR = args.write_base_addr TRANSP_MODE = args.transp_mode + MATRIX_DIM_C = args.num_channels MATRIX_DIM_N = args.matrix_dim_n MATRIX_DIM_M = args.matrix_dim_m TOT_LENGTH = (args.matrix_dim_m * args.matrix_dim_n) // BANDWIDTH_ELEMS OUTPUT_DIR = args.output_dir - if MEMORY_SIZE < ((MATRIX_DIM_N * MATRIX_DIM_M * ELEM_WIDTH // WORD_WIDTH) * 2): + if MEMORY_SIZE < ((MATRIX_DIM_C * MATRIX_DIM_N * MATRIX_DIM_M * ELEM_WIDTH // WORD_WIDTH) * 2): raise ValueError(f"MEMORY_SIZE ({MEMORY_SIZE}) is too small for the given matrix size " - f"({MATRIX_DIM_M}x{MATRIX_DIM_N}) and element width ({ELEM_WIDTH})") + f"({MATRIX_DIM_C}x{MATRIX_DIM_M}x{MATRIX_DIM_N}) and element width ({ELEM_WIDTH})") # num_elem_word must be power of two and greater than zero if args.num_elem_word & (args.num_elem_word - 1) != 0 or args.num_elem_word <= 0: raise ValueError("[GM] num_elem_word must be a power of two and greater than zero.") @@ -402,13 +405,14 @@ def main(): 'word_width': WORD_WIDTH, 'elem_width': args.elem_width, 'memory_size': args.mem_size, - 'misaligned_accesses': args.misaligned_accesses, + # 'misaligned_accesses': args.misaligned_accesses, 'datamover_mode': args.datamover_mode, 'transp_mode': args.transp_mode, 'cim_mode': args.cim_mode, 'cim_inner_dim': args.cim_inner_dim, 'cim_outer_dim': args.cim_outer_dim, 'matrix_dim_m': args.matrix_dim_m, + 'num_channels': args.num_channels, 'matrix_dim_n': args.matrix_dim_n } write_data_header_file(OUTPUT_DIR, input_matrix, output_matrix, config_params) diff --git a/verif/python/validate_config.py b/verif/python/validate_config.py index a87b5c7..2532dec 100755 --- a/verif/python/validate_config.py +++ b/verif/python/validate_config.py @@ -7,14 +7,14 @@ import sys import argparse -def validate_config(bandwidth, word_width, elem_width, memory_size, misaligned_accesses, +def validate_config(bandwidth, word_width, elem_width, memory_size, num_channels, datamover_mode, transp_mode, cim_mode, cim_inner_dim, cim_outer_dim, matrix_dim_m, matrix_dim_n): """Validate configuration parameters""" errors = [] warnings = [] - bandwidth_aligned = bandwidth - (word_width if misaligned_accesses else 0) + bandwidth_aligned = bandwidth # Computed values bandwidth_elems = bandwidth_aligned // elem_width @@ -27,9 +27,9 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, misaligned_a if word_width % elem_width != 0: errors.append(f"WORD_WIDTH ({word_width}) must be divisible by ELEM_WIDTH ({elem_width})") - if memory_size < (matrix_dim_n * matrix_dim_m * elem_width // word_width) * 2: + if memory_size < (num_channels * matrix_dim_n * matrix_dim_m * elem_width // word_width) * 2: errors.append(f"MEMORY_SIZE ({memory_size}) is too small for the given matrix size " - f"({matrix_dim_m}x{matrix_dim_n}) and element width ({elem_width})") + f"({num_channels}x{matrix_dim_m}x{matrix_dim_n}) and element width ({elem_width})") # Mode validation (based on config.mk) if datamover_mode not in [0, 1, 2, 3]: @@ -59,13 +59,13 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, misaligned_a # errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) cannot be greater than matrix height ({matrix_dim_m})") # Memory requirements - matrix_elements = matrix_dim_m * matrix_dim_n + matrix_elements = num_channels * matrix_dim_m * matrix_dim_n matrix_words = (matrix_elements * elem_width + word_width - 1) // word_width total_memory_needed = matrix_words * 2 # Input + output matrices if total_memory_needed > memory_size: errors.append(f"Memory size ({memory_size} words) insufficient for matrices " - f"({total_memory_needed} words needed for {matrix_dim_m}x{matrix_dim_n} input+output)") + f"({total_memory_needed} words needed for {num_channels}x{matrix_dim_m}x{matrix_dim_n} input+output)") # Matrix dimension alignment errors # if matrix_dim_n % bandwidth_elems != 0: @@ -90,7 +90,7 @@ def main(): parser.add_argument("--word_width", type=int, required=True) parser.add_argument("--elem_width", type=int, required=True) parser.add_argument("--memory_size", type=int, required=True) - parser.add_argument("--misaligned_accesses", type=int, required=True) + parser.add_argument("--num_channels", type=int, required=True) parser.add_argument("--datamover_mode", type=int, required=True) parser.add_argument("--transp_mode", type=int, required=True) parser.add_argument("--cim_mode", type=int, required=True) @@ -103,7 +103,7 @@ def main(): errors, warnings = validate_config( args.bandwidth, args.word_width, args.elem_width, args.memory_size, - args.misaligned_accesses, args.datamover_mode, args.transp_mode, args.cim_mode, + args.num_channels, args.datamover_mode, args.transp_mode, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, args.matrix_dim_m, args.matrix_dim_n ) @@ -138,12 +138,12 @@ def main(): print(f" CIM_OUTER_DIM: {args.cim_outer_dim}") # Print computed values - bandwidth_aligned = args.bandwidth - (args.word_width if args.misaligned_accesses else 0) - bandwidth_elems = bandwidth_aligned // args.elem_width + bandwidth_elems = args.bandwidth // args.elem_width num_elem_word = args.word_width // args.elem_width - matrix_words = (args.matrix_dim_m * args.matrix_dim_n) // num_elem_word + matrix_words = (args.num_channels * args.matrix_dim_m * args.matrix_dim_n) // num_elem_word print(f"\nComputed values:") + print(f" Channels: {args.num_channels}") print(f" Elements per bandwidth: {bandwidth_elems}") print(f" Elements per word: {num_elem_word}") print(f" Matrix memory usage: {matrix_words} words ({matrix_words * 2} total)") diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index 0f569eb..e09c7e5 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -30,6 +30,7 @@ import tb_package::*; logic clear_i = 1'b0; logic randomize_mem = 1'b0; + logic enable_mem = 1'b1; logic stallable_mem = 1'b1; hwpe_stream_intf_tcdm #( @@ -67,6 +68,8 @@ import tb_package::*; logic [11:0] matrix_dim_n; logic [3:0] read_dim_enable; logic [3:0] write_dim_enable; + logic [10:0] num_channels; + logic [20:0] total_elements; // Performs one entire clock cycle. task cycle; @@ -130,6 +133,8 @@ import tb_package::*; assign matrix_dim_n = `STIM_MATRIX_DIM_N; assign read_dim_enable = `STIM_READ_DIM_ENABLE; assign write_dim_enable = `STIM_WRITE_DIM_ENABLE; + assign num_channels = `STIM_NUM_CHANNELS; + assign total_elements = `STIM_TOTAL_ELEMENTS; datamover_top_wrap #( @@ -234,7 +239,8 @@ import tb_package::*; initial begin : main_execution logic [31:0] ctrl_engine_reg; - logic [31:0] dim_enable_reg; + logic [31:0] matrix_dim_reg; + logic [31:0] channels_reg; $info("Start execution...\n"); @@ -264,8 +270,9 @@ import tb_package::*; periph_write(datamover_package::DATAMOVER_REG_OUT_PTR, datamover_package::DATAMOVER_REGISTER_OFFS, write_addr.base_addr, clk_i, periph_bus); // Configure packed length registers (see datamover_package.sv) - ctrl_engine_reg = {5'b0, matrix_dim_n[11:0], matrix_dim_m[11:0], transp_mode[2:0]}; - dim_enable_reg = {24'b0, write_dim_enable[3:0], read_dim_enable[3:0]}; + ctrl_engine_reg = {16'b0, write_dim_enable[3:0], read_dim_enable[3:0], 5'b0, transp_mode[2:0]}; + matrix_dim_reg = {matrix_dim_n[15:0], matrix_dim_m[15:0]}; + channels_reg = {total_elements[20:0], num_channels[10:0]}; // Make sure tot_length is the same for read and write assert (read_addr.tot_length == write_addr.tot_length) else $fatal("Read and write total lengths do not match!"); @@ -280,7 +287,8 @@ import tb_package::*; periph_write(datamover_package::DATAMOVER_REG_OUT_D2, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d2_stride, write_addr.d2_length}, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_OUT_D3, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d3_stride, write_addr.d3_length}, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_IN_OUT_D4_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d4_stride, read_addr.d4_stride}, clk_i, periph_bus); - periph_write(datamover_package::DATAMOVER_REG_DIM_ENABLE, datamover_package::DATAMOVER_REGISTER_OFFS, dim_enable_reg, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_MATRIX_DIM, datamover_package::DATAMOVER_REGISTER_OFFS, matrix_dim_reg, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_CHANNELS, datamover_package::DATAMOVER_REGISTER_OFFS, channels_reg, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_CTRL_ENGINE, datamover_package::DATAMOVER_REGISTER_OFFS, ctrl_engine_reg, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_COMMIT_AND_TRIGGER, datamover_package::HWPE_REGISTER_OFFS, 32'h0, clk_i, periph_bus); diff --git a/verif/tb/tb_package.sv b/verif/tb/tb_package.sv index 7a01151..b03b41c 100644 --- a/verif/tb/tb_package.sv +++ b/verif/tb/tb_package.sv @@ -189,8 +189,8 @@ package tb_package; if (read_data !== golden_data) begin status = 1; $display("MISMATCH at address %0d: Expected %h, Actual %h", start_addr + i, golden_data, read_data); - end else begin - // $display("MATCH at address %0d: %h", start_addr + i, read_data); + end else if (i<100) begin//else begin + $display("MATCH at address %0d: %h", start_addr + i, read_data); end end From d58a985413c9036c65dc84d15e01289cc3b6a6c6 Mon Sep 17 00:00:00 2001 From: cdurrer Date: Thu, 16 Apr 2026 13:42:38 +0200 Subject: [PATCH 29/29] Renamed matrix to tensor after introducing channel dimension, code cleanup --- CONFIG_USAGE.md | 112 ++-- Makefile | 111 ++-- README.md | 4 +- config.mk | 113 ++-- config_presets.mk | 147 +++-- modelsim/Makefile | 7 - rtl/datamover_engine.sv | 125 ++-- rtl/datamover_package.sv | 13 +- rtl/datamover_streamer.sv | 8 +- rtl/datamover_top.sv | 48 +- rtl/datamover_top_wrap.sv | 12 +- test/datamover_test.c | 193 ++++++ test/hal_datamover.c | 551 ++++++++++++++++++ test/hal_datamover.h | 291 +++++---- test/hal_hwpe.c | 47 ++ test/hal_hwpe.h | 65 +++ test/lfsr32.c | 153 ----- test/lfsr32.h | 25 - test/test_datamover.c | 125 ---- verif/python/datamover_golden_model.py | 224 +++++++ verif/python/datamover_golden_model_numpy.py | 21 +- .../datamover_microarchitectural_model.py | 2 + verif/python/generate_stimuli.py | 123 ++-- verif/python/validate_config.py | 58 +- verif/tb/tb_datamover_top_wrap.sv | 20 +- verif/tb/tb_package.sv | 7 +- 26 files changed, 1634 insertions(+), 971 deletions(-) create mode 100644 test/datamover_test.c create mode 100644 test/hal_datamover.c create mode 100644 test/hal_hwpe.c create mode 100644 test/hal_hwpe.h delete mode 100644 test/lfsr32.c delete mode 100644 test/lfsr32.h delete mode 100644 test/test_datamover.c create mode 100644 verif/python/datamover_golden_model.py diff --git a/CONFIG_USAGE.md b/CONFIG_USAGE.md index 607eeaa..f5a191f 100644 --- a/CONFIG_USAGE.md +++ b/CONFIG_USAGE.md @@ -1,5 +1,6 @@ # Configuration Examples and Usage Guide +NOTE: OUTDATED TESTING METHODOLOGY! This document describes how to use the flexible configuration system for the datamover HWPE project. ## 🚀 Quick Commands @@ -12,7 +13,7 @@ make help make validate-config # Run with preset -make sim CONFIG_PRESET=small-matrix +make sim CONFIG_PRESET=small-tensor # Override specific parameter make sim CONFIG_PRESET=transpose-test TRANSP_MODE=4 @@ -35,19 +36,19 @@ make test-cim-grid Run simulations with predefined configurations: ```bash -# Small matrix for quick testing -make sim CONFIG_PRESET=small-matrix +# Small tensor for quick testing +make sim CONFIG_PRESET=small-tensor -# Medium matrix for moderate testing -make sim CONFIG_PRESET=medium-matrix +# Medium tensor for moderate testing +make sim CONFIG_PRESET=medium-tensor -# Large matrix for stress testing -make sim CONFIG_PRESET=large-matrix +# Large tensor for stress testing +make sim CONFIG_PRESET=large-tensor # Transpose-focused testing make sim CONFIG_PRESET=transpose-test -# Rectangular matrix testing +# Rectangular tensor testing make sim CONFIG_PRESET=rect-wide make sim CONFIG_PRESET=rect-tall make sim CONFIG_PRESET=rect-narrow @@ -68,14 +69,14 @@ make sim CONFIG_PRESET=cim-large Override specific parameters while keeping preset base: ```bash -# Use small-matrix preset but change transpose mode -make sim CONFIG_PRESET=small-matrix TRANSP_MODE=2 +# Use small-tensor preset but change transpose mode +make sim CONFIG_PRESET=small-tensor TRANSP_MODE=2 -# Use medium-matrix preset but different element width -make sim CONFIG_PRESET=medium-matrix ELEM_WIDTH=16 +# Use medium-tensor preset but different element width +make sim CONFIG_PRESET=medium-tensor ELEM_WIDTH=16 -# Override matrix dimensions -make sim CONFIG_PRESET=transpose-test MATRIX_DIM_M=64 MATRIX_DIM_N=64 +# Override tensor dimensions +make sim CONFIG_PRESET=transpose-test TENSOR_SIZE_M=64 TENSOR_SIZE_N=64 ``` ### Custom Configuration @@ -84,7 +85,7 @@ Use completely custom parameters: ```bash # Custom configuration via command line -make sim CONFIG_PRESET=custom BANDWIDTH=512 ELEM_WIDTH=16 MATRIX_DIM_M=128 MATRIX_DIM_N=128 +make sim CONFIG_PRESET=custom BANDWIDTH=512 ELEM_WIDTH=16 TENSOR_SIZE_M=128 TENSOR_SIZE_N=128 # Or edit config.mk for persistent custom settings make sim CONFIG_PRESET=custom @@ -100,11 +101,11 @@ Parameters are resolved in this order (highest priority first): ## Available Presets -| Preset | Description | Matrix Size | Memory | Mode | +| Preset | Description | Tensor Size | Memory | Mode | |--------|-------------|-------------|--------|------| -| `small-matrix` | Quick testing | 4x4 | 2KB | Transpose | -| `medium-matrix` | Moderate testing | 64x64 | 16KB | Transpose | -| `large-matrix` | Stress testing | 448x448 | 512KB | Transpose | +| `small-tensor` | Quick testing | 4x4 | 2KB | Transpose | +| `medium-tensor` | Moderate testing | 64x64 | 16KB | Transpose | +| `large-tensor` | Stress testing | 448x448 | 512KB | Transpose | | `transpose-test` | Transpose focus | 32x32 | 64KB | Transpose | | `rect-wide` | Wide rectangle | 64x256 | 128KB | Transpose | | `rect-tall` | Tall rectangle | 256x64 | 128KB | Transpose | @@ -128,17 +129,17 @@ The datamover supports three main operation modes: - **Example**: `make sim CONFIG_PRESET=copy-small` ### Transpose Mode (DATAMOVER_MODE=1) -- **Purpose**: Matrix transposition during data movement +- **Purpose**: Tensor transposition during data movement - **Use case**: Data layout transformations for optimized access patterns - **Transpose elements**: 1, 2, or 4 elements per cycle (`TRANSP_MODE`) -- **Presets**: `small-matrix`, `medium-matrix`, `large-matrix`, `transpose-test`, `rect-*` +- **Presets**: `small-tensor`, `medium-tensor`, `large-tensor`, `transpose-test`, `rect-*` - **Example**: `make sim CONFIG_PRESET=transpose-test TRANSP_MODE=2` ### CIM Mode (DATAMOVER_MODE=2) - **Purpose**: Compute-In-Memory data layout conversion - **Use case**: Converting row-major data to CIM accelerator layouts -- **CIM layouts**: A-Layout (`CIM_MODE=0`) or B-Layout (`CIM_MODE=1`) -- **Dimensions**: Configurable `CIM_INNER_DIM` and `CIM_OUTER_DIM` +- **CIM layout**: Row-major -> CIM-layout (`CIM_MODE=0`) or CIM-layout -> row-major (`CIM_MODE=1`) +- **Dimensions**: Configurable `ROW_TILE_SIZE` - **Presets**: `cim-small`, `cim-medium`, `cim-large` - **Example**: `make sim CONFIG_PRESET=cim-medium` @@ -146,15 +147,14 @@ The datamover supports three main operation modes: | Parameter | Values | Description | |-----------|---------|-------------| -| `DATAMOVER_MODE` | 0,1,2 | Operation mode (0=Copy, 1=Transpose, 2=CIM) | +| `DATAMOVER_MODE` | 0,1,2 | Operation mode (0=Copy, 1=Transpose, 2=CIM layout conversion, 3=CIM transpose, 4=unfold, 5=fold) | | `TRANSP_MODE` | 0,1,2,4 | Transpose elements per cycle | -| `CIM_MODE` | 0,1 | CIM layout (0=A-Layout, 1=B-Layout) | -| `CIM_INNER_DIM` | 32,64,... | CIM inner dimension in elements | -| `CIM_OUTER_DIM` | 16,32,64,... | CIM outer dimension in elements | +| `CIM_MODE` | 0,1 | CIM layout (0=row-major->CIM-Layout, 1=CIM-Layout->row_major) | +| `ROW_TILE_SIZE` | 32,64,... | Row tile size in elements | | `ELEM_WIDTH` | 8 | Element width in bits | | `BANDWIDTH` | 64,128,256,512,1024 | Memory bandwidth in bits | -| `MATRIX_DIM_M` | Any | Matrix height in elements | -| `MATRIX_DIM_N` | Any | Matrix width in elements | +| `TENSOR_SIZE_M` | Any | Tensor height in elements | +| `TENSOR_SIZE_N` | Any | Tensor width in elements | | `MEMORY_SIZE` | Any | Available memory in words | | `WORD_WIDTH` | 16,32,64 | Word width in bits (typically 32) | @@ -205,8 +205,8 @@ ifeq ($(CONFIG_PRESET),my-test) ELEM_WIDTH = 16 MEMORY_SIZE = 32768 TRANSP_MODE = 4 - MATRIX_DIM_M = 64 - MATRIX_DIM_N = 32 + TENSOR_SIZE_M = 64 + TENSOR_SIZE_N = 32 CONFIG_DESC = "Custom test for specific use case" endif ``` @@ -237,9 +237,9 @@ The system includes the following built-in test targets: # Built-in targets (already available) test-all-presets: # Tests all 8 configuration presets (stops on first failure) - $(MAKE) sim CONFIG_PRESET=small-matrix - $(MAKE) sim CONFIG_PRESET=medium-matrix - $(MAKE) sim CONFIG_PRESET=large-matrix + $(MAKE) sim CONFIG_PRESET=small-tensor + $(MAKE) sim CONFIG_PRESET=medium-tensor + $(MAKE) sim CONFIG_PRESET=large-tensor $(MAKE) sim CONFIG_PRESET=transpose-test $(MAKE) sim CONFIG_PRESET=rect-wide $(MAKE) sim CONFIG_PRESET=rect-tall @@ -264,9 +264,9 @@ For additional custom test suites, you can create your own targets: # Add to your Makefile for custom test suites test-custom-suite: @echo "Testing custom configuration suite..." - $(MAKE) sim CONFIG_PRESET=small-matrix TRANSP_MODE=2 - $(MAKE) sim CONFIG_PRESET=medium-matrix ELEM_WIDTH=16 - $(MAKE) sim CONFIG_PRESET=custom BANDWIDTH=1024 MATRIX_DIM_M=64 MATRIX_DIM_N=64 + $(MAKE) sim CONFIG_PRESET=small-tensor TRANSP_MODE=2 + $(MAKE) sim CONFIG_PRESET=medium-tensor ELEM_WIDTH=16 + $(MAKE) sim CONFIG_PRESET=custom BANDWIDTH=1024 TENSOR_SIZE_M=64 TENSOR_SIZE_N=64 ``` ### Configuration Validation @@ -274,7 +274,7 @@ test-custom-suite: The system includes automatic validation: - TRANSP_MODE must be 0, 1, 2, or 4 -- Matrix dimensions must fit in available memory +- Tensor dimensions must fit in available memory - Bandwidth and element width must be compatible ### Debug Configuration @@ -286,19 +286,19 @@ To see all computed values and configuration info: make help # Validate configuration with detailed output -make validate-config CONFIG_PRESET=small-matrix +make validate-config CONFIG_PRESET=small-tensor # Enable debug output in config.mk (uncomment $(info) lines) -make sim CONFIG_PRESET=small-matrix | grep -E "(BANDWIDTH|MATRIX|STIM)" +make sim CONFIG_PRESET=small-tensor | grep -E "(BANDWIDTH|TENSOR|STIM)" ``` ## 💡 Usage Patterns ```bash # Development cycle -make sim CONFIG_PRESET=small-matrix # Quick check +make sim CONFIG_PRESET=small-tensor # Quick check make sim CONFIG_PRESET=transpose-test # Feature test -make sim CONFIG_PRESET=large-matrix # Stress test +make sim CONFIG_PRESET=large-tensor # Stress test # Research/tuning make sim CONFIG_PRESET=custom BANDWIDTH=1024 ELEM_WIDTH=32 @@ -310,26 +310,26 @@ make help ## Tips and Best Practices -1. **Start Small**: Use `small-matrix` for initial testing, then scale up +1. **Start Small**: Use `small-tensor` for initial testing, then scale up 2. **Test Transpose**: Use `transpose-test` preset for transpose functionality 3. **Rectangular Testing**: Use rectangular presets (`rect-wide`, `rect-tall`, etc.) for non-square matrices 4. **Parameter Validation**: Always run `make validate-config` to check computed values 5. **Documentation**: Document custom presets with clear descriptions -6. **Memory Requirements**: Ensure matrix dimensions fit within available memory -7. **Bandwidth Alignment**: Both matrix dimensions should be ≥ BANDWIDTH/ELEM_WIDTH +6. **Memory Requirements**: Ensure tensor dimensions fit within available memory +7. **Bandwidth Alignment**: Both tensor dimensions should be ≥ BANDWIDTH/ELEM_WIDTH ## Example Workflows ### Development Workflow ```bash # Quick functionality check -make sim CONFIG_PRESET=small-matrix +make sim CONFIG_PRESET=small-tensor # Detailed transpose testing make sim CONFIG_PRESET=transpose-test # Stress testing with large matrices -make sim CONFIG_PRESET=large-matrix +make sim CONFIG_PRESET=large-tensor ``` ### CI/Testing Workflow @@ -351,8 +351,8 @@ make test-cim-grid make sim CONFIG_PRESET=custom \ BANDWIDTH=1024 \ ELEM_WIDTH=32 \ - MATRIX_DIM_M=256 \ - MATRIX_DIM_N=128 \ + TENSOR_SIZE_M=256 \ + TENSOR_SIZE_N=128 \ TRANSP_MODE=4 ``` @@ -375,20 +375,20 @@ The configuration system consists of these key files: make validate-config CONFIG_PRESET=my-preset # Check for errors ``` -2. **Memory insufficient**: Matrix too large for available memory +2. **Memory insufficient**: Tensor too large for available memory ```bash - # Reduce matrix size or increase MEMORY_SIZE - make sim CONFIG_PRESET=small-matrix # Use smaller preset + # Reduce tensor size or increase MEMORY_SIZE + make sim CONFIG_PRESET=small-tensor # Use smaller preset ``` -3. **Bandwidth alignment warnings**: Matrix dimensions not aligned to bandwidth +3. **Bandwidth alignment warnings**: Tensor dimensions not aligned to bandwidth ```bash - # Adjust matrix dimensions to be multiples of BANDWIDTH/ELEM_WIDTH - make sim MATRIX_DIM_M=32 MATRIX_DIM_N=32 # Use aligned dimensions + # Adjust tensor dimensions to be multiples of BANDWIDTH/ELEM_WIDTH + make sim TENSOR_SIZE_M=32 TENSOR_SIZE_N=32 # Use aligned dimensions ``` 4. **Configuration not taking effect**: Check parameter precedence ```bash # Command line overrides presets - make sim CONFIG_PRESET=small-matrix TRANSP_MODE=2 # Override works + make sim CONFIG_PRESET=small-tensor TRANSP_MODE=2 # Override works ``` diff --git a/Makefile b/Makefile index c0c221f..83b85db 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,9 @@ # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 +# Standalone testing setup OUTDATED! +# ToDo: Implement SW-based testing with a CPU core + include config.mk SHELL = /usr/bin/env bash @@ -64,8 +67,8 @@ TESTBENCH_DEFINES += -DSTIM_MEM_SIZE=${STIM_MEM_SIZE} TESTBENCH_DEFINES += -DSTIM_TRANSP_MODE=${STIM_TRANSP_MODE} # TESTBENCH_DEFINES += -DSTIM_TRANSP_LEN=${STIM_TRANSP_LEN} -TESTBENCH_DEFINES += -DSTIM_MATRIX_DIM_M=${STIM_MATRIX_DIM_M} -TESTBENCH_DEFINES += -DSTIM_MATRIX_DIM_N=${STIM_MATRIX_DIM_N} +TESTBENCH_DEFINES += -DSTIM_TENSOR_SIZE_M=${STIM_TENSOR_SIZE_M} +TESTBENCH_DEFINES += -DSTIM_TENSOR_SIZE_N=${STIM_TENSOR_SIZE_N} TESTBENCH_DEFINES += -DSTIM_NUM_CHANNELS=${STIM_NUM_CHANNELS} TESTBENCH_DEFINES += -DSTIM_TOTAL_ELEMENTS=${STIM_TOTAL_ELEMENTS} @@ -86,25 +89,25 @@ help: @echo "==========================================" @echo "" @echo "Available presets:" - @echo " small-matrix : 4x4 matrix (transpose)" - @echo " medium-matrix : 64x64 matrix (transpose)" - @echo " large-matrix : 448x448 matrix (transpose)" - @echo " transpose-test : 32x32 matrix (transpose)" - @echo " rect-wide : 64x256 wide rectangular matrix (transpose)" - @echo " rect-tall : 256x64 tall rectangular matrix (transpose)" - @echo " rect-narrow : 16x128 narrow rectangular matrix (transpose)" - @echo " rect-elongated : 128x32 elongated rectangular matrix (transpose)" - @echo " copy-small : 4x4 matrix (copy mode)" - @echo " copy-medium : 64x64 matrix (copy mode)" - @echo " cim-small : 32x128 matrix (CIM mode)" - @echo " cim-medium : 64x256 matrix (CIM mode)" - @echo " cim-large : 128x256 matrix (CIM mode)" + @echo " small-tensor : 4x4 tensor (transpose)" + @echo " medium-tensor : 64x64 tensor (transpose)" + @echo " large-tensor : 448x448 tensor (transpose)" + @echo " transpose-test : 32x32 tensor (transpose)" + @echo " rect-wide : 64x256 wide rectangular tensor (transpose)" + @echo " rect-tall : 256x64 tall rectangular tensor (transpose)" + @echo " rect-narrow : 16x128 narrow rectangular tensor (transpose)" + @echo " rect-elongated : 128x32 elongated rectangular tensor (transpose)" + @echo " copy-small : 4x4 tensor (copy mode)" + @echo " copy-medium : 64x64 tensor (copy mode)" + @echo " cim-small : 32x128 tensor (CIM mode)" + @echo " cim-medium : 64x256 tensor (CIM mode)" + @echo " cim-large : 128x256 tensor (CIM mode)" @echo " custom : User-defined (config.mk default)" @echo "" @echo "Usage examples:" - @echo " make sim CONFIG_PRESET=small-matrix" + @echo " make sim CONFIG_PRESET=small-tensor" @echo " make sim CONFIG_PRESET=transpose-test TRANSP_MODE=2" - @echo " make sim MATRIX_DIM_M=64 MATRIX_DIM_N=32" + @echo " make sim TENSOR_SIZE_M=64 TENSOR_SIZE_N=32" @echo "" @echo "Test targets:" @echo " make test-all-presets : Test all presets (detailed reporting)" @@ -119,7 +122,7 @@ help: test-all-presets: @echo "Testing all configuration presets..." @failed_tests=""; \ - for preset in small-matrix medium-matrix large-matrix transpose-test rect-wide rect-tall rect-narrow rect-elongated copy-small copy-medium cim-small cim-medium cim-large; do \ + for preset in small-tensor medium-tensor large-tensor transpose-test rect-wide rect-tall rect-narrow rect-elongated copy-small copy-medium cim-small cim-medium cim-large; do \ echo "=== Testing CONFIG_PRESET=$$preset ==="; \ if $(MAKE) sim CONFIG_PRESET=$$preset; then \ echo "✓ $$preset: PASSED"; \ @@ -169,7 +172,7 @@ test-transpose-grid: for word_width in 16 32 64; do \ total_tests=$$((total_tests + 1)); \ echo "=== Testing BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width ==="; \ - if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ + if $(MAKE) sim CONFIG_PRESET=medium-tensor BANDWIDTH=$$bandwidth DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ echo "✓ BANDWIDTH=$$bandwidth TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: PASSED"; \ passed_tests=$$((passed_tests + 1)); \ else \ @@ -203,7 +206,7 @@ test-transpose-grid-misaligned: for word_width in 32; do \ total_tests=$$((total_tests + 1)); \ echo "=== Testing BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width ==="; \ - if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ + if $(MAKE) sim CONFIG_PRESET=medium-tensor BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ echo "✓ BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: PASSED"; \ passed_tests=$$((passed_tests + 1)); \ else \ @@ -218,7 +221,7 @@ test-transpose-grid-misaligned: for word_width in 64; do \ total_tests=$$((total_tests + 1)); \ echo "=== Testing BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width ==="; \ - if $(MAKE) sim CONFIG_PRESET=medium-matrix BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ + if $(MAKE) sim CONFIG_PRESET=medium-tensor BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 DATAMOVER_MODE=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width; then \ echo "✓ BANDWIDTH=$$bandwidth MISALIGNED_ACCESSES=1 TRANSP_MODE=$$transp_mode WORD_WIDTH=$$word_width: PASSED"; \ passed_tests=$$((passed_tests + 1)); \ else \ @@ -249,35 +252,31 @@ test-cim-grid: passed_tests=0; \ for bandwidth in 128 256; do \ for word_width in 32 64; do \ - for cim_inner_dim in 32 64; do \ - for cim_outer_dim in 32 64; do \ - total_tests=$$((total_tests + 1)); \ - echo "=== Testing BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim ==="; \ - if $(MAKE) sim CONFIG_PRESET=cim-large DATAMOVER_MODE=2 CIM_MODE=0 BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim; then \ - echo "✓ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim: PASSED"; \ - passed_tests=$$((passed_tests + 1)); \ - else \ - echo "✗ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim: FAILED"; \ - failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/WORD_WIDTH=$$word_width/CIM_INNER_DIM=$$cim_inner_dim/CIM_OUTER_DIM=$$cim_outer_dim"; \ - fi; \ - done; \ + for row_tile_size in 32 64; do \ + total_tests=$$((total_tests + 1)); \ + echo "=== Testing BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width ROW_TILE_SIZE=$$row_tile_size ==="; \ + if $(MAKE) sim CONFIG_PRESET=cim-large DATAMOVER_MODE=2 CIM_MODE=0 BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width ROW_TILE_SIZE=$$row_tile_size; then \ + echo "✓ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width ROW_TILE_SIZE=$$row_tile_size: PASSED"; \ + passed_tests=$$((passed_tests + 1)); \ + else \ + echo "✗ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width ROW_TILE_SIZE=$$row_tile_size: FAILED"; \ + failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/WORD_WIDTH=$$word_width/ROW_TILE_SIZE=$$row_tile_size"; \ + fi; \ done; \ done; \ done; \ for bandwidth in 512; do \ for word_width in 32 64; do \ - for cim_inner_dim in 64; do \ - for cim_outer_dim in 64; do \ - total_tests=$$((total_tests + 1)); \ - echo "=== Testing BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim ==="; \ - if $(MAKE) sim CONFIG_PRESET=cim-large DATAMOVER_MODE=2 CIM_MODE=0 BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim; then \ - echo "✓ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim: PASSED"; \ - passed_tests=$$((passed_tests + 1)); \ - else \ - echo "✗ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width CIM_INNER_DIM=$$cim_inner_dim CIM_OUTER_DIM=$$cim_outer_dim: FAILED"; \ - failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/WORD_WIDTH=$$word_width/CIM_INNER_DIM=$$cim_inner_dim/CIM_OUTER_DIM=$$cim_outer_dim"; \ - fi; \ - done; \ + for row_tile_size in 64; do \ + total_tests=$$((total_tests + 1)); \ + echo "=== Testing BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width ROW_TILE_SIZE=$$row_tile_size ==="; \ + if $(MAKE) sim CONFIG_PRESET=cim-large DATAMOVER_MODE=2 CIM_MODE=0 BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width ROW_TILE_SIZE=$$row_tile_size; then \ + echo "✓ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width ROW_TILE_SIZE=$$row_tile_size: PASSED"; \ + passed_tests=$$((passed_tests + 1)); \ + else \ + echo "✗ BANDWIDTH=$$bandwidth WORD_WIDTH=$$word_width ROW_TILE_SIZE=$$row_tile_size: FAILED"; \ + failed_tests="$$failed_tests BANDWIDTH=$$bandwidth/WORD_WIDTH=$$word_width/ROW_TILE_SIZE=$$row_tile_size"; \ + fi; \ done; \ done; \ done; \ @@ -295,9 +294,6 @@ test-cim-grid: echo "====== SUMMARY: All CIM configuration combinations PASSED! ======"; \ fi -# ToDo: CIM tests grid - - # Validate current configuration validate-config: @echo "Validating current configuration..." @@ -306,14 +302,13 @@ validate-config: --word_width $(WORD_WIDTH) \ --elem_width $(ELEM_WIDTH) \ --memory_size $(MEMORY_SIZE) \ - --num_channels $(MATRIX_DIM_C) \ --datamover_mode $(DATAMOVER_MODE) \ --transp_mode $(TRANSP_MODE) \ --cim_mode $(CIM_MODE) \ - --cim_inner_dim $(CIM_INNER_DIM) \ - --cim_outer_dim $(CIM_OUTER_DIM) \ - --matrix_dim_m $(MATRIX_DIM_M) \ - --matrix_dim_n $(MATRIX_DIM_N) + --row_tile_size $(ROW_TILE_SIZE) \ + --size_m $(TENSOR_SIZE_M) \ + --size_n $(TENSOR_SIZE_N) \ + --num_channels $(NUM_CHANNELS) clean-sim: rm -rf $(SIM_PATH)/work @@ -342,15 +337,13 @@ stimuli: clean-stimuli validate-config --bandwidth_bits $(BANDWIDTH) \ --num_elem_word $(NUM_ELEM_WORD) \ --elem_width $(ELEM_WIDTH) \ - --misaligned_accesses $(MISALIGNED_ACCESSES) \ --datamover_mode $(DATAMOVER_MODE) \ --transp_mode $(STIM_TRANSP_MODE) \ --cim_mode $(CIM_MODE) \ - --cim_inner_dim $(CIM_INNER_DIM) \ - --cim_outer_dim $(CIM_OUTER_DIM) \ - --matrix_dim_m $(MATRIX_DIM_M) \ - --matrix_dim_n $(MATRIX_DIM_N) \ - --num_channels $(MATRIX_DIM_C) \ + --row_tile_size $(ROW_TILE_SIZE) \ + --size_m $(TENSOR_SIZE_M) \ + --size_n $(TENSOR_SIZE_N) \ + --num_channels $(NUM_CHANNELS) \ --output_dir "verif/python/generated" # Bender diff --git a/README.md b/README.md index b8e4566..c0a592d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Configure the hardware and testbench parameters in `config.mk`. - `NUM_ELEM_WORD * ELEM_WIDTH` is the width of a memory bank word. `BANDWIDTH` must be divisible by such word width, as `BANDWIDTH / (NUM_ELEM_WORD * ELEM_WIDTH)` is the number of banks accessed in parallel by the HWPE in one memory access. - `NUM_ELEM_WORD` must be a power of two due to memory addressing. Currently, the configurations `NUM_ELEM_WORD` = 2,4 support the datamover's transposition mode (1 elem, 2 elems, 4 elems). `NUM_ELEM_WORD` = 1 and `NUM_ELEM_WORD` > 4 is not supported. -### Testbench parameters +### Testbench parameters - OUTDATED The following parameters are used to generate the testbench stimuli and to configure the datamover registers. @@ -66,7 +66,7 @@ make sim ``` By default QuestaSim GUI is active. You can simulate the RTL in CLI mode with `GUI=0 make sim`. -## Testing and Validation +## Testing and Validation - OUTDATED The datamover HWPE provides several test targets for comprehensive validation: diff --git a/config.mk b/config.mk index 52aabd7..b2eb938 100644 --- a/config.mk +++ b/config.mk @@ -5,6 +5,9 @@ # This file contains the configuration parameter for # the standalone simulation of the datamover HWPE +# Standalone testing setup OUTDATED! +# ToDo: Implement SW-based testing with a CPU core + # Include configuration presets (optional) -include config_presets.mk @@ -17,18 +20,17 @@ BANDWIDTH ?= 512 # in bits, multiple of WORD_WIDTH (512) WORD_WIDTH ?= 64 # in bits, multiple of ELEM_WIDTH (64) ELEM_WIDTH ?= 8 # in bits (8) MEMORY_SIZE ?= 131072 # in words -# MISALIGNED_ACCESSES ?= 0 +MISALIGNED_ACCESSES ?= 0 DATAMOVER_MODE ?= 1 # 0 = copy, 1 = transpose, 2 = CIM data layout conversion, 3 = CIM data layout transpose, 4 = unfold (MobileViT), 5 = fold (MobileViT), other values: not accepted TRANSP_MODE ?= 1 # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted -CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> A-Layout, 1: reverse (A-Layout -> row-major), ToDo (unnecessary for 64x64 CIM): 2: row-major -> B-Layout, 3: reverse (B-Layout -> row-major) -CIM_INNER_DIM ?= 64 # Inner dimension of the CIM accelerator (in elements): 64 for 64x8 CIM macro -CIM_OUTER_DIM ?= 64 # Outer dimension of the CIM accelerator (in elements): 8x #CIM macros +CIM_MODE ?= 0 # Data layout conversion mode: 0: row-major -> CIM-Layout, 1: reverse (CIM-Layout -> row-major) - This parameter is not passed to the HW, only used in the HAL +ROW_TILE_SIZE ?= 64 # Row tile size for CIM data layout (in elements): 64 for 64x8 CIM macro -# Input matrix dimensions (in elements) -NUM_CHANNELS ?= 1 # Number of channels (ToDo: currently, this should be set to 1 except for unfold mode) -MATRIX_DIM_M ?= 64 # Matrix height in elements -MATRIX_DIM_N ?= 64 # Matrix width in elements +# Input tensor dimensions (in elements) +NUM_CHANNELS ?= 1 # Number of channels (ToDo: currently, this should be set to 1 except for unfold and fold modes) +TENSOR_SIZE_M ?= 64 # Matrix height in elements +TENSOR_SIZE_N ?= 64 # Matrix width in elements READ_BASE_ADDR = 0 @@ -38,28 +40,28 @@ READ_BASE_ADDR = 0 BANDWIDTH_ALIGNED := $(BANDWIDTH) # in bits BANDWIDTH_ELEMS := $(shell echo $$(($(BANDWIDTH_ALIGNED) / $(ELEM_WIDTH)))) # Number of elements per bandwidth NUM_ELEM_WORD := $(shell echo $$(($(WORD_WIDTH) / $(ELEM_WIDTH)))) # Number of elements per word -MATRIX_SIZE_TOT := $(shell echo $$(($(MATRIX_DIM_M) * $(MATRIX_DIM_N)))) # Total number of elements in the matrix -TOTAL_ELEMENTS := $(shell echo $$(($(NUM_CHANNELS) * $(MATRIX_SIZE_TOT)))) # Total number of elements in all channels -MATRIX_MISALIGNED := $(shell echo $$(($(TOTAL_ELEMENTS) % $(BANDWIDTH_ELEMS)))) # 1 if matrix size is not multiple of bandwidth elements +TENSOR_SIZE_TOT := $(shell echo $$(($(TENSOR_SIZE_M) * $(TENSOR_SIZE_N)))) # Total number of elements in the tensor +TOTAL_ELEMENTS := $(shell echo $$(($(NUM_CHANNELS) * $(TENSOR_SIZE_TOT)))) # Total number of elements in all channels +MATRIX_MISALIGNED := $(shell echo $$(($(TOTAL_ELEMENTS) % $(BANDWIDTH_ELEMS)))) # 1 if tensor size is not multiple of bandwidth elements ifeq "$(strip $(MATRIX_MISALIGNED))" "0" # Matrix size aligned - TOTAL_ACCESSES := $(shell echo $$(($(TOTAL_ELEMENTS) / $(BANDWIDTH_ELEMS)))) # Total number of memory accesses (words) for the matrix (floor division) + TOTAL_ACCESSES := $(shell echo $$(($(TOTAL_ELEMENTS) / $(BANDWIDTH_ELEMS)))) # Total number of memory accesses (words) for the tensor (floor division) else # Matrix size misaligned - TOTAL_ACCESSES := $(shell echo $$(($(TOTAL_ELEMENTS) / $(BANDWIDTH_ELEMS) + 1))) # Total number of memory accesses (words) for the matrix (+1 for misaligned access) + TOTAL_ACCESSES := $(shell echo $$(($(TOTAL_ELEMENTS) / $(BANDWIDTH_ELEMS) + 1))) # Total number of memory accesses (words) for the tensor (+1 for misaligned access) endif WRITE_BASE_ADDR = $(shell echo $$(($(READ_BASE_ADDR) + $(TOTAL_ELEMENTS)))) # Element-addressed -# Align matrix dimensions to bandwidth for transposition (fill elem_matrix) -MATRIX_DIM_M_MOD := $(shell echo $$(( $(MATRIX_DIM_M) % $(BANDWIDTH_ELEMS) ))) -MATRIX_DIM_N_MOD := $(shell echo $$(( $(MATRIX_DIM_N) % $(BANDWIDTH_ELEMS) ))) -ifeq ($(MATRIX_DIM_M_MOD),0) - MATRIX_DIM_M_ALIGNED := $(MATRIX_DIM_M) +# Align tensor dimensions to bandwidth for transposition (fill elem_matrix) +TENSOR_SIZE_M_MOD := $(shell echo $$(( $(TENSOR_SIZE_M) % $(BANDWIDTH_ELEMS) ))) +TENSOR_SIZE_N_MOD := $(shell echo $$(( $(TENSOR_SIZE_N) % $(BANDWIDTH_ELEMS) ))) +ifeq ($(TENSOR_SIZE_M_MOD),0) + TENSOR_SIZE_M_ALIGNED := $(TENSOR_SIZE_M) else - MATRIX_DIM_M_ALIGNED := $(shell echo $$(($(MATRIX_DIM_M) + $(BANDWIDTH_ELEMS) - $(MATRIX_DIM_M_MOD)))) # Align M dimension to bandwidth + TENSOR_SIZE_M_ALIGNED := $(shell echo $$(($(TENSOR_SIZE_M) + $(BANDWIDTH_ELEMS) - $(TENSOR_SIZE_M_MOD)))) # Align M dimension to bandwidth endif -ifeq ($(MATRIX_DIM_N_MOD),0) - MATRIX_DIM_N_ALIGNED := $(MATRIX_DIM_N) +ifeq ($(TENSOR_SIZE_N_MOD),0) + TENSOR_SIZE_N_ALIGNED := $(TENSOR_SIZE_N) else - MATRIX_DIM_N_ALIGNED := $(shell echo $$(($(MATRIX_DIM_N) + $(BANDWIDTH_ELEMS) - $(MATRIX_DIM_N_MOD)))) # Align N dimension to bandwidth + TENSOR_SIZE_N_ALIGNED := $(shell echo $$(($(TENSOR_SIZE_N) + $(BANDWIDTH_ELEMS) - $(TENSOR_SIZE_N_MOD)))) # Align N dimension to bandwidth endif # ADDR and STRIDE are in bytes, LENGTH is in number of memory accesses (bandwidth) @@ -67,7 +69,7 @@ endif ifeq "$(strip $(DATAMOVER_MODE))" "0" # Copy mode $(info Copy mode enabled) STIM_READ_BASE_ADDR ?= $(READ_BASE_ADDR) # Element-addressed -STIM_READ_D0_LENGTH ?= $(TOTAL_ACCESSES) # [Nof accesses with bandwidth BW per D0-transfer ("row")] ToDo(cdurrer): not working for misaligned matrices +STIM_READ_D0_LENGTH ?= $(TOTAL_ACCESSES) # [Nof accesses with bandwidth BW per D0-transfer ("row")] STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] STIM_READ_D1_LENGTH ?= 0 STIM_READ_D1_STRIDE ?= 0 @@ -82,8 +84,8 @@ STIM_READ_DIM_ENABLE ?= "4'b0000" STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= 0 # STIM_TRANSP_LEN ?= 0 -STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) -STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) +STIM_TENSOR_SIZE_M ?= $(TENSOR_SIZE_M) +STIM_TENSOR_SIZE_N ?= $(TENSOR_SIZE_N) STIM_NUM_CHANNELS ?= 1 STIM_TOTAL_ELEMENTS ?= $(TOTAL_ELEMENTS) @@ -107,37 +109,37 @@ ifneq ($(filter 1 2 4,$(strip $(TRANSP_MODE))), $(strip $(TRANSP_MODE))) endif STIM_READ_BASE_ADDR ?= $(READ_BASE_ADDR) # Element-addressed -STIM_READ_D0_LENGTH ?= $(MATRIX_DIM_M_ALIGNED) # [Nof accesses with bandwidth BW per D0-transfer ("row")] -STIM_READ_D0_STRIDE ?= $(MATRIX_DIM_N) # [Elements] -STIM_READ_D1_LENGTH ?= $(shell echo $$(($(MATRIX_DIM_N_ALIGNED) / $(BANDWIDTH_ELEMS)))) # [Number of full D0-transfers ("rows")] +STIM_READ_D0_LENGTH ?= $(TENSOR_SIZE_M_ALIGNED) # [Nof accesses with bandwidth BW per D0-transfer ("row")] +STIM_READ_D0_STRIDE ?= $(TENSOR_SIZE_N) # [Elements] +STIM_READ_D1_LENGTH ?= $(shell echo $$(($(TENSOR_SIZE_N_ALIGNED) / $(BANDWIDTH_ELEMS)))) # [Number of full D0-transfers ("rows")] STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -> manually compute "next row" stride STIM_READ_D2_LENGTH ?= 0 STIM_READ_D2_STRIDE ?= 0 STIM_READ_D3_LENGTH ?= 0 STIM_READ_D3_STRIDE ?= 0 STIM_READ_D4_STRIDE ?= 0 -STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] TODO(cdurrer): FIX +STIM_READ_TOT_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(STIM_READ_D1_LENGTH)))) # [Total memory accesses] STIM_READ_DIM_ENABLE ?= "4'b0001" STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= $(TRANSP_MODE) # 1 = 1 elem, 2 = 2 elem, 4 = 4 elem, other values: not accepted # STIM_TRANSP_LEN ?= 0 # If 0: BANDWIDTH_ALIGNED / ELEM_WIDTH -STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) # Actual (non-aligned) matrix dimensions -STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) +STIM_TENSOR_SIZE_M ?= $(TENSOR_SIZE_M) # Actual (non-aligned) tensor dimensions +STIM_TENSOR_SIZE_N ?= $(TENSOR_SIZE_N) STIM_NUM_CHANNELS ?= 1 STIM_TOTAL_ELEMENTS ?= $(TOTAL_ELEMENTS) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(BANDWIDTH_ELEMS) / $(TRANSP_MODE)))) # Transpose tile width corresponds to bandwidth -STIM_WRITE_D0_STRIDE ?= $(shell echo $$(($(MATRIX_DIM_M) * $(TRANSP_MODE)))) # Transpose: Input matrix height corresponds to output matrix width -STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(STIM_WRITE_D0_STRIDE) / $(BANDWIDTH_ELEMS))))# Transpose: Input matrix height corresponds to output matrix width +STIM_WRITE_D0_STRIDE ?= $(shell echo $$(($(TENSOR_SIZE_M) * $(TRANSP_MODE)))) # Transpose: Input tensor height corresponds to output tensor width +STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(STIM_WRITE_D0_STRIDE) / $(BANDWIDTH_ELEMS))))# Transpose: Input tensor height corresponds to output tensor width STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) # Transpose tile height corresponds to bandwidth STIM_WRITE_D2_LENGTH ?= 0 STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(STIM_WRITE_D0_STRIDE) * $(STIM_WRITE_D0_LENGTH)))) # D2 length is controlled by total length STIM_WRITE_D3_LENGTH ?= 0 STIM_WRITE_D3_STRIDE ?= 0 STIM_WRITE_D4_STRIDE ?= 0 -STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) # Same total length as read !!! TODO(cdurrer): FIX +STIM_WRITE_TOT_LENGTH ?= $(STIM_READ_TOT_LENGTH) STIM_WRITE_DIM_ENABLE ?= "4'b0011" else ifeq "$(strip $(DATAMOVER_MODE))" "2" # CIM data layout conversion mode @@ -146,12 +148,12 @@ ifneq ($(filter 0 1, $(strip $(CIM_MODE))), $(strip $(CIM_MODE))) $(error "Invalid CIM_MODE $(CIM_MODE): must be 0 or 1") endif STIM_READ_BASE_ADDR ?= $(READ_BASE_ADDR) # Element-addressed -STIM_READ_D0_LENGTH ?= $(shell echo $$(($(CIM_INNER_DIM) / $(BANDWIDTH_ELEMS)))) # [Nof accesses with bandwidth BW per D0-transfer ("row")] +STIM_READ_D0_LENGTH ?= $(shell echo $$(($(ROW_TILE_SIZE) / $(BANDWIDTH_ELEMS)))) # [Nof accesses with bandwidth BW per D0-transfer ("row")] STIM_READ_D0_STRIDE ?= $(BANDWIDTH_ELEMS) # [Elements] -STIM_READ_D1_LENGTH ?= $(MATRIX_DIM_M) -STIM_READ_D1_STRIDE ?= $(MATRIX_DIM_N) -STIM_READ_D2_LENGTH ?= $(shell echo $$(($(MATRIX_DIM_N) / $(CIM_INNER_DIM)))) # Redundant (handled by TOT_LEN) -STIM_READ_D2_STRIDE ?= $(CIM_INNER_DIM) +STIM_READ_D1_LENGTH ?= $(TENSOR_SIZE_M) +STIM_READ_D1_STRIDE ?= $(TENSOR_SIZE_N) +STIM_READ_D2_LENGTH ?= $(shell echo $$(($(TENSOR_SIZE_N) / $(ROW_TILE_SIZE)))) # Redundant (handled by TOT_LEN) +STIM_READ_D2_STRIDE ?= $(ROW_TILE_SIZE) STIM_READ_D3_LENGTH ?= 0 STIM_READ_D3_STRIDE ?= 0 STIM_READ_D4_STRIDE ?= 0 @@ -162,15 +164,15 @@ STIM_READ_DIM_ENABLE ?= "4'b0011" STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= 0 # STIM_TRANSP_LEN ?= 0 -STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) -STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) +STIM_TENSOR_SIZE_M ?= $(TENSOR_SIZE_M) +STIM_TENSOR_SIZE_N ?= $(TENSOR_SIZE_N) STIM_NUM_CHANNELS ?= 1 STIM_TOTAL_ELEMENTS ?= $(TOTAL_ELEMENTS) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed -STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(MATRIX_DIM_M)))) +STIM_WRITE_D0_LENGTH ?= $(shell echo $$(($(STIM_READ_D0_LENGTH) * $(TENSOR_SIZE_M)))) STIM_WRITE_D0_STRIDE ?= $(BANDWIDTH_ELEMS) -STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(MATRIX_DIM_N) / $(CIM_INNER_DIM)))) +STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(TENSOR_SIZE_N) / $(ROW_TILE_SIZE)))) STIM_WRITE_D1_STRIDE ?= $(shell echo $$(($(STIM_WRITE_D0_LENGTH) * $(BANDWIDTH_ELEMS)))) STIM_WRITE_D2_LENGTH ?= 0 STIM_WRITE_D2_STRIDE ?= 0 @@ -190,12 +192,12 @@ ifneq ($(filter 1 2 4,$(strip $(TRANSP_MODE))), $(strip $(TRANSP_MODE))) endif STIM_READ_BASE_ADDR ?= $(READ_BASE_ADDR) # Element-addressed -STIM_READ_D0_LENGTH ?= $(MATRIX_DIM_M) -STIM_READ_D0_STRIDE ?= $(CIM_INNER_DIM) -STIM_READ_D1_LENGTH ?= $(shell echo $$(($(CIM_INNER_DIM) / $(BANDWIDTH_ELEMS)))) +STIM_READ_D0_LENGTH ?= $(TENSOR_SIZE_M) +STIM_READ_D0_STRIDE ?= $(ROW_TILE_SIZE) +STIM_READ_D1_LENGTH ?= $(shell echo $$(($(ROW_TILE_SIZE) / $(BANDWIDTH_ELEMS)))) STIM_READ_D1_STRIDE ?= $(BANDWIDTH_ELEMS) STIM_READ_D2_LENGTH ?= 0 # Not used (controlled by TOT_LEN) -STIM_READ_D2_STRIDE ?= $(shell echo $$(($(MATRIX_DIM_M) * $(CIM_INNER_DIM)))) +STIM_READ_D2_STRIDE ?= $(shell echo $$(($(TENSOR_SIZE_M) * $(ROW_TILE_SIZE)))) STIM_READ_D3_LENGTH ?= 0 STIM_READ_D3_STRIDE ?= 0 STIM_READ_D4_STRIDE ?= 0 @@ -205,18 +207,18 @@ STIM_READ_DIM_ENABLE ?= "4'b0011" STIM_MEM_SIZE ?= $(MEMORY_SIZE) # [Words] STIM_TRANSP_MODE ?= $(TRANSP_MODE) # transp_mode # STIM_TRANSP_LEN ?= 0 -STIM_MATRIX_DIM_M ?= $(MATRIX_DIM_M) -STIM_MATRIX_DIM_N ?= $(MATRIX_DIM_N) +STIM_TENSOR_SIZE_M ?= $(TENSOR_SIZE_M) +STIM_TENSOR_SIZE_N ?= $(TENSOR_SIZE_N) STIM_NUM_CHANNELS ?= 1 STIM_TOTAL_ELEMENTS ?= $(TOTAL_ELEMENTS) STIM_WRITE_BASE_ADDR ?= $(WRITE_BASE_ADDR) # Element-addressed STIM_WRITE_D0_LENGTH ?= $(BANDWIDTH_ELEMS) -STIM_WRITE_D0_STRIDE ?= $(CIM_INNER_DIM) -STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(CIM_INNER_DIM) / $(BANDWIDTH_ELEMS)))) +STIM_WRITE_D0_STRIDE ?= $(ROW_TILE_SIZE) +STIM_WRITE_D1_LENGTH ?= $(shell echo $$(($(ROW_TILE_SIZE) / $(BANDWIDTH_ELEMS)))) STIM_WRITE_D1_STRIDE ?= $(BANDWIDTH_ELEMS) STIM_WRITE_D2_LENGTH ?= 0 -STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(BANDWIDTH_ELEMS) * $(CIM_INNER_DIM)))) +STIM_WRITE_D2_STRIDE ?= $(shell echo $$(($(BANDWIDTH_ELEMS) * $(ROW_TILE_SIZE)))) STIM_WRITE_D3_LENGTH ?= 0 STIM_WRITE_D3_STRIDE ?= 0 STIM_WRITE_D4_STRIDE ?= 0 @@ -241,12 +243,11 @@ $(info NUM_ELEM_WORD: $(NUM_ELEM_WORD)) $(info DATAMOVER_MODE: $(DATAMOVER_MODE)) $(info TRANSP_MODE: $(TRANSP_MODE)) $(info CIM_MODE: $(CIM_MODE)) -$(info CIM_INNER_DIM: $(CIM_INNER_DIM) elements) -$(info CIM_OUTER_DIM: $(CIM_OUTER_DIM) elements) +$(info ROW_TILE_SIZE: $(ROW_TILE_SIZE) elements) $(info ) $(info Matrix Configuration:) -$(info MATRIX_SIZE: $(MATRIX_DIM_M) x $(MATRIX_DIM_N)) -$(info MATRIX SIZE ALIGNED: $(MATRIX_DIM_M_ALIGNED) x $(MATRIX_DIM_N_ALIGNED)) +$(info MATRIX_SIZE: $(TENSOR_SIZE_M) x $(TENSOR_SIZE_N)) +$(info MATRIX SIZE ALIGNED: $(TENSOR_SIZE_M_ALIGNED) x $(TENSOR_SIZE_N_ALIGNED)) $(info MEMORY_SIZE: $(MEMORY_SIZE) words) $(info STIM_MEM_SIZE: $(STIM_MEM_SIZE) words) $(info ) diff --git a/config_presets.mk b/config_presets.mk index d8ffe38..971cc27 100644 --- a/config_presets.mk +++ b/config_presets.mk @@ -10,26 +10,26 @@ ######################################### # Available presets: -# - small-matrix : Small 4x4 matrix for quick testing (transpose) -# - medium-matrix : Medium 64x64 matrix for moderate testing (transpose) -# - large-matrix : Large 448x448 matrix for stress testing (transpose) +# - small-tensor : Small 4x4 tensor for quick testing (transpose) +# - medium-tensor : Medium 64x64 tensor for moderate testing (transpose) +# - large-tensor : Large 448x448 tensor for stress testing (transpose) # - transpose-test : Optimized for transpose functionality verification -# - rect-wide : Wide rectangular matrix (64x256) (transpose) -# - rect-tall : Tall rectangular matrix (256x64) (transpose) -# - rect-narrow : Narrow rectangular matrix (16x128) (transpose) -# - rect-elongated : Elongated rectangular matrix (128x32) (transpose) -# - copy-small : Small 4x4 matrix for copy mode testing -# - copy-medium : Medium 64x64 matrix for copy mode testing -# - cim-small : CIM 32x128 matrix, CIM_INNER_DIM=32, 128-bit bandwidth -# - cim-medium : CIM 64x256 matrix, CIM_INNER_DIM=64, 256-bit bandwidth -# - cim-large : CIM 128x256 matrix, CIM_INNER_DIM=64, 512-bit bandwidth +# - rect-wide : Wide rectangular tensor (64x256) (transpose) +# - rect-tall : Tall rectangular tensor (256x64) (transpose) +# - rect-narrow : Narrow rectangular tensor (16x128) (transpose) +# - rect-elongated : Elongated rectangular tensor (128x32) (transpose) +# - copy-small : Small 4x4 tensor for copy mode testing +# - copy-medium : Medium 64x64 tensor for copy mode testing +# - cim-small : CIM 32x128 tensor, ROW_TILE_SIZE=32, 128-bit bandwidth +# - cim-medium : CIM 64x256 tensor, ROW_TILE_SIZE=64, 256-bit bandwidth +# - cim-large : CIM 128x256 tensor, ROW_TILE_SIZE=64, 512-bit bandwidth # - custom : User-defined configuration (default) # Select configuration preset (can be overridden via command line) CONFIG_PRESET ?= custom # Preset-specific configurations -ifeq ($(CONFIG_PRESET),small-matrix) +ifeq ($(CONFIG_PRESET),small-tensor) BANDWIDTH = 32 WORD_WIDTH = 32 ELEM_WIDTH = 8 @@ -38,14 +38,13 @@ ifeq ($(CONFIG_PRESET),small-matrix) DATAMOVER_MODE = 1 TRANSP_MODE = 1 CIM_MODE = 0 - CIM_INNER_DIM = 0 - CIM_OUTER_DIM = 0 - MATRIX_DIM_M = 4 - MATRIX_DIM_N = 4 - CONFIG_DESC = "Small 4x4 matrix, 1-element transpose" + ROW_TILE_SIZE = 0 + TENSOR_SIZE_M = 4 + TENSOR_SIZE_N = 4 + CONFIG_DESC = "Small 4x4 tensor, 1-element transpose" endif -ifeq ($(CONFIG_PRESET),medium-matrix) +ifeq ($(CONFIG_PRESET),medium-tensor) BANDWIDTH = 128 WORD_WIDTH = 32 ELEM_WIDTH = 8 @@ -54,14 +53,13 @@ ifeq ($(CONFIG_PRESET),medium-matrix) DATAMOVER_MODE = 1 TRANSP_MODE = 1 CIM_MODE = 0 - CIM_INNER_DIM = 0 - CIM_OUTER_DIM = 0 - MATRIX_DIM_M = 64 - MATRIX_DIM_N = 64 - CONFIG_DESC = "Medium 64x64 matrix, 1-element transpose" + ROW_TILE_SIZE = 0 + TENSOR_SIZE_M = 64 + TENSOR_SIZE_N = 64 + CONFIG_DESC = "Medium 64x64 tensor, 1-element transpose" endif -ifeq ($(CONFIG_PRESET),large-matrix) +ifeq ($(CONFIG_PRESET),large-tensor) BANDWIDTH = 512 WORD_WIDTH = 32 ELEM_WIDTH = 8 @@ -70,11 +68,10 @@ ifeq ($(CONFIG_PRESET),large-matrix) DATAMOVER_MODE = 1 TRANSP_MODE = 1 CIM_MODE = 0 - CIM_INNER_DIM = 0 - CIM_OUTER_DIM = 0 - MATRIX_DIM_M = 448 - MATRIX_DIM_N = 448 - CONFIG_DESC = "Large 448x448 matrix, 1-element transpose" + ROW_TILE_SIZE = 0 + TENSOR_SIZE_M = 448 + TENSOR_SIZE_N = 448 + CONFIG_DESC = "Large 448x448 tensor, 1-element transpose" endif ifeq ($(CONFIG_PRESET),transpose-test) @@ -86,11 +83,10 @@ ifeq ($(CONFIG_PRESET),transpose-test) DATAMOVER_MODE = 1 TRANSP_MODE = 2 CIM_MODE = 0 - CIM_INNER_DIM = 0 - CIM_OUTER_DIM = 0 - MATRIX_DIM_M = 32 - MATRIX_DIM_N = 32 - CONFIG_DESC = "32x32 matrix, 2-element transpose" + ROW_TILE_SIZE = 0 + TENSOR_SIZE_M = 32 + TENSOR_SIZE_N = 32 + CONFIG_DESC = "32x32 tensor, 2-element transpose" endif ifeq ($(CONFIG_PRESET),rect-wide) @@ -102,11 +98,10 @@ ifeq ($(CONFIG_PRESET),rect-wide) DATAMOVER_MODE = 1 TRANSP_MODE = 4 CIM_MODE = 0 - CIM_INNER_DIM = 0 - CIM_OUTER_DIM = 0 - MATRIX_DIM_M = 64 - MATRIX_DIM_N = 256 - CONFIG_DESC = "Wide rectangular matrix 64x256, 4-element transpose" + ROW_TILE_SIZE = 0 + TENSOR_SIZE_M = 64 + TENSOR_SIZE_N = 256 + CONFIG_DESC = "Wide rectangular tensor 64x256, 4-element transpose" endif ifeq ($(CONFIG_PRESET),rect-tall) @@ -118,11 +113,10 @@ ifeq ($(CONFIG_PRESET),rect-tall) DATAMOVER_MODE = 1 TRANSP_MODE = 2 CIM_MODE = 0 - CIM_INNER_DIM = 0 - CIM_OUTER_DIM = 0 - MATRIX_DIM_M = 256 - MATRIX_DIM_N = 64 - CONFIG_DESC = "Tall rectangular matrix 256x64, 2-element transpose" + ROW_TILE_SIZE = 0 + TENSOR_SIZE_M = 256 + TENSOR_SIZE_N = 64 + CONFIG_DESC = "Tall rectangular tensor 256x64, 2-element transpose" endif ifeq ($(CONFIG_PRESET),rect-narrow) @@ -134,11 +128,10 @@ ifeq ($(CONFIG_PRESET),rect-narrow) DATAMOVER_MODE = 1 TRANSP_MODE = 1 CIM_MODE = 0 - CIM_INNER_DIM = 0 - CIM_OUTER_DIM = 0 - MATRIX_DIM_M = 16 - MATRIX_DIM_N = 128 - CONFIG_DESC = "Narrow rectangular matrix 16x128, 1-element transpose" + ROW_TILE_SIZE = 0 + TENSOR_SIZE_M = 16 + TENSOR_SIZE_N = 128 + CONFIG_DESC = "Narrow rectangular tensor 16x128, 1-element transpose" endif ifeq ($(CONFIG_PRESET),rect-elongated) @@ -150,11 +143,10 @@ ifeq ($(CONFIG_PRESET),rect-elongated) DATAMOVER_MODE = 1 TRANSP_MODE = 2 CIM_MODE = 0 - CIM_INNER_DIM = 0 - CIM_OUTER_DIM = 0 - MATRIX_DIM_M = 128 - MATRIX_DIM_N = 32 - CONFIG_DESC = "Elongated rectangular matrix 128x32, 2-element transpose" + ROW_TILE_SIZE = 0 + TENSOR_SIZE_M = 128 + TENSOR_SIZE_N = 32 + CONFIG_DESC = "Elongated rectangular tensor 128x32, 2-element transpose" endif ifeq ($(CONFIG_PRESET),copy-small) @@ -166,11 +158,10 @@ ifeq ($(CONFIG_PRESET),copy-small) DATAMOVER_MODE = 0 TRANSP_MODE = 0 CIM_MODE = 0 - CIM_INNER_DIM = 0 - CIM_OUTER_DIM = 0 - MATRIX_DIM_M = 4 - MATRIX_DIM_N = 4 - CONFIG_DESC = "Small 4x4 matrix, copy mode" + ROW_TILE_SIZE = 0 + TENSOR_SIZE_M = 4 + TENSOR_SIZE_N = 4 + CONFIG_DESC = "Small 4x4 tensor, copy mode" endif ifeq ($(CONFIG_PRESET),copy-medium) @@ -182,11 +173,10 @@ ifeq ($(CONFIG_PRESET),copy-medium) DATAMOVER_MODE = 0 TRANSP_MODE = 0 CIM_MODE = 0 - CIM_INNER_DIM = 0 - CIM_OUTER_DIM = 0 - MATRIX_DIM_M = 64 - MATRIX_DIM_N = 64 - CONFIG_DESC = "Medium 64x64 matrix, copy mode" + ROW_TILE_SIZE = 0 + TENSOR_SIZE_M = 64 + TENSOR_SIZE_N = 64 + CONFIG_DESC = "Medium 64x64 tensor, copy mode" endif ifeq ($(CONFIG_PRESET),cim-small) @@ -198,11 +188,10 @@ ifeq ($(CONFIG_PRESET),cim-small) DATAMOVER_MODE = 2 TRANSP_MODE = 0 CIM_MODE = 0 - CIM_INNER_DIM = 32 - CIM_OUTER_DIM = 16 - MATRIX_DIM_M = 32 - MATRIX_DIM_N = 128 - CONFIG_DESC = "CIM 32x128 matrix, CIM_INNER_DIM=32, 128-bit bandwidth" + ROW_TILE_SIZE = 32 + TENSOR_SIZE_M = 32 + TENSOR_SIZE_N = 128 + CONFIG_DESC = "CIM 32x128 tensor, ROW_TILE_SIZE=32, 128-bit bandwidth" endif ifeq ($(CONFIG_PRESET),cim-medium) @@ -214,11 +203,10 @@ ifeq ($(CONFIG_PRESET),cim-medium) DATAMOVER_MODE = 2 TRANSP_MODE = 0 CIM_MODE = 0 - CIM_INNER_DIM = 64 - CIM_OUTER_DIM = 32 - MATRIX_DIM_M = 64 - MATRIX_DIM_N = 256 - CONFIG_DESC = "CIM 64x256 matrix, CIM_INNER_DIM=64, 256-bit bandwidth" + ROW_TILE_SIZE = 64 + TENSOR_SIZE_M = 64 + TENSOR_SIZE_N = 256 + CONFIG_DESC = "CIM 64x256 tensor, ROW_TILE_SIZE=64, 256-bit bandwidth" endif ifeq ($(CONFIG_PRESET),cim-large) @@ -230,11 +218,10 @@ ifeq ($(CONFIG_PRESET),cim-large) DATAMOVER_MODE = 2 TRANSP_MODE = 0 CIM_MODE = 0 - CIM_INNER_DIM = 64 - CIM_OUTER_DIM = 64 - MATRIX_DIM_M = 128 - MATRIX_DIM_N = 256 - CONFIG_DESC = "CIM 128x256 matrix, CIM_INNER_DIM=64, 512-bit bandwidth" + ROW_TILE_SIZE = 64 + TENSOR_SIZE_M = 128 + TENSOR_SIZE_N = 256 + CONFIG_DESC = "CIM 128x256 tensor, ROW_TILE_SIZE=64, 512-bit bandwidth" endif ifeq ($(CONFIG_PRESET),custom) diff --git a/modelsim/Makefile b/modelsim/Makefile index b05e5ea..db7baf7 100644 --- a/modelsim/Makefile +++ b/modelsim/Makefile @@ -21,13 +21,6 @@ else VSIM_FLAGS += -c endif -VSIM_FLAGS ?= -ifeq ($(GUI), 1) - VSIM_FLAGS += -gui -else - VSIM_FLAGS += -c -endif - lib: cd $(buildpath) && $(VLIB) work && $(VMAP) work work diff --git a/rtl/datamover_engine.sv b/rtl/datamover_engine.sv index b547ecb..6237efb 100644 --- a/rtl/datamover_engine.sv +++ b/rtl/datamover_engine.sv @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 ETH Zurich and University of Bologna + * Copyright (C) 2025-2026 ETH Zurich and University of Bologna * * Copyright and related rights are licensed under the Solderpad Hardware * License, Version 0.51 (the "License"); you may not use this file except in @@ -32,9 +32,9 @@ module datamover_engine // global signals input logic clk_i, input logic rst_ni, - input logic test_mode_i, // ToDo: unused + input logic test_mode_i, // unused // local enable & clear - input logic enable_i, // ToDo: unused + input logic enable_i, // unused input logic clear_i, // control registers input ctrl_engine_t ctrl_i, @@ -61,15 +61,13 @@ module datamover_engine logic [NB_ELEMENTS-1:0][ELEM_WIDTH-1:0] data_out_unrolled; logic data_out_valid; logic data_out_ready; - logic [23:0] matrix_tot_size; logic [NB_ELEM_LOG2-1:0] remaining_elems; logic [17:0] total_accesses_copy_mode, total_accesses; - logic [15:0] c_elem_cnt, expanded_c_elems; - logic [15:0] m_elem_cnt, expanded_m_elems; - logic [9:0] c_tiles, m_tiles, n_tiles, n_tile_cnt; + logic [15:0] y_elem_cnt, expanded_y_elems; + logic [9:0] y_tiles, n_tiles, n_tile_cnt; logic [NB_ELEM_LOG2:0] leftover_rows, leftover_cols; - logic last_c_tile, last_m_tile, last_n_tile; - logic write_to_buffer_done, read_from_buffer_done; + logic last_y_tile, last_n_tile; + logic execution_done; // FSM: WRITE -> READ on input handshake at end of write, READ -> WRITE on output handshake at end of read @@ -79,13 +77,11 @@ module datamover_engine case (fsm_q) WRITE: begin if (((cnt_q == ctrl_i.transp_len-ctrl_i.transp_stride)) && (data_in_valid & data_in_ready)) begin - // if ((write_to_buffer_done || (cnt_q == ctrl_i.transp_len-ctrl_i.transp_stride)) && (data_in_valid & data_in_ready)) begin fsm_d = READ; end end READ: begin if (((cnt_q == ctrl_i.transp_len-ctrl_i.transp_stride)) && (data_out_valid & data_out_ready)) begin - // if ((read_from_buffer_done || (cnt_q == ctrl_i.transp_len-ctrl_i.transp_stride)) && (data_out_valid & data_out_ready)) begin fsm_d = WRITE; end end @@ -156,55 +152,41 @@ module datamover_engine // Partial tile / leftover elements handling // Due to the streamer address generation, matrices need to be word-aligned in n-dimension for transposition localparam logic [NB_ELEMENTS-1:0] STRB_ONE = {{(NB_ELEMENTS-1){1'b0}}, 1'b1}; // Necessary to force the shifting operation to the correct bitwidth (default would be only 32b) - assign matrix_tot_size = ctrl_i.total_elements; // Pre-computed by HAL: num_channels * dim_m * dim_n - assign remaining_elems = matrix_tot_size & (NB_ELEMENTS - 1); // modulo (NB_ELEMENTS: power of two) - this signal is only used in copy mode - assign total_accesses_copy_mode = (matrix_tot_size >> NB_ELEM_LOG2) + ((remaining_elems != 0) ? 1 : 0); - - assign c_tiles = (ctrl_i.num_channels + NB_ELEMENTS - 1) >> NB_ELEM_LOG2; // ceil division // ToDo: combine c_tile signals with m_tiles according to mode (only one necessary at a time) - assign m_tiles = (ctrl_i.matrix_dim_m + NB_ELEMENTS - 1) >> NB_ELEM_LOG2; // ceil division - assign n_tiles = (ctrl_i.matrix_dim_n + NB_ELEMENTS - 1) >> NB_ELEM_LOG2; // ceil division - assign total_accesses = (ctrl_i.datamover_mode == DATAMOVER_UNFOLD || ctrl_i.datamover_mode == DATAMOVER_FOLD) ? (c_tiles * ctrl_i.matrix_dim_m * n_tiles) << NB_ELEM_LOG2 : (m_tiles * n_tiles) << NB_ELEM_LOG2; // NB_ELEMENTS is a power of 2, so multiply by shifting; ToDo: remaining MUL overhead, could be pre-computed in HAL and configured in control register - assign leftover_rows = (ctrl_i.datamover_mode == DATAMOVER_UNFOLD || ctrl_i.datamover_mode == DATAMOVER_FOLD) ? ctrl_i.num_channels & (NB_ELEMENTS - 1) : ctrl_i.matrix_dim_m & (NB_ELEMENTS - 1); - assign leftover_cols = ctrl_i.matrix_dim_n & (NB_ELEMENTS - 1); - assign expanded_c_elems = c_tiles << NB_ELEM_LOG2; - assign expanded_m_elems = m_tiles << NB_ELEM_LOG2; - assign c_elem_cnt = (expanded_c_elems == 0) ? '0 : (tot_cnt_q % expanded_c_elems); // ToDo: restructure without modulo (pre-compute in HAL?) - assign m_elem_cnt = (expanded_m_elems == 0) ? '0 : (tot_cnt_q % expanded_m_elems); // ToDo: restructure without modulo (pre-compute in HAL?) - assign n_tile_cnt = (ctrl_i.datamover_mode == DATAMOVER_UNFOLD || ctrl_i.datamover_mode == DATAMOVER_FOLD) ? (tot_cnt_q / expanded_c_elems) : (tot_cnt_q / expanded_m_elems); // ToDo: restructure without division (pre-compute in HAL?) - assign last_c_tile = ((c_elem_cnt >> NB_ELEM_LOG2) >= (ctrl_i.num_channels >> NB_ELEM_LOG2)); - assign last_m_tile = ((m_elem_cnt >> NB_ELEM_LOG2) >= (ctrl_i.matrix_dim_m >> NB_ELEM_LOG2)); - assign last_n_tile = (n_tile_cnt >= (ctrl_i.matrix_dim_n >> NB_ELEM_LOG2)); + assign remaining_elems = ctrl_i.total_elements & (NB_ELEMENTS - 1); // modulo (NB_ELEMENTS: power of two) - this signal is only used in copy mode + assign total_accesses_copy_mode = (ctrl_i.total_elements >> NB_ELEM_LOG2) + ((remaining_elems != 0) ? 1 : 0); + + // y_tiles represents the number of tiles in c-dimension for unfold/fold modes, and the number of tiles in m-dimension for all other modes + assign y_tiles = (ctrl_i.datamover_mode == DATAMOVER_UNFOLD || ctrl_i.datamover_mode == DATAMOVER_FOLD) ? + (ctrl_i.num_channels + NB_ELEMENTS - 1) >> NB_ELEM_LOG2 : + (ctrl_i.tensor_size_m + NB_ELEMENTS - 1) >> NB_ELEM_LOG2; // ceil division + assign n_tiles = (ctrl_i.tensor_size_n + NB_ELEMENTS - 1) >> NB_ELEM_LOG2; // ceil division + assign total_accesses = (ctrl_i.datamover_mode == DATAMOVER_UNFOLD || ctrl_i.datamover_mode == DATAMOVER_FOLD) ? (y_tiles * ctrl_i.tensor_size_m * n_tiles) << NB_ELEM_LOG2 : (y_tiles * n_tiles) << NB_ELEM_LOG2; // NB_ELEMENTS is a power of 2, so multiply by shifting; ToDo: remaining MUL overhead, could be pre-computed in HAL and configured in control register + assign leftover_rows = (ctrl_i.datamover_mode == DATAMOVER_UNFOLD || ctrl_i.datamover_mode == DATAMOVER_FOLD) ? ctrl_i.num_channels & (NB_ELEMENTS - 1) : ctrl_i.tensor_size_m & (NB_ELEMENTS - 1); + assign leftover_cols = ctrl_i.tensor_size_n & (NB_ELEMENTS - 1); + assign expanded_y_elems = y_tiles << NB_ELEM_LOG2; // taking into account partial tiles + assign y_elem_cnt = (expanded_y_elems == 0) ? '0 : (tot_cnt_q % expanded_y_elems); // ToDo: restructure without modulo (pre-compute in HAL?) + assign n_tile_cnt = (expanded_y_elems == 0) ? '0 : (tot_cnt_q / expanded_y_elems); // ToDo: restructure without division (pre-compute in HAL?) + assign last_y_tile = (ctrl_i.datamover_mode == DATAMOVER_UNFOLD || ctrl_i.datamover_mode == DATAMOVER_FOLD) ? + ((y_elem_cnt >> NB_ELEM_LOG2) >= (ctrl_i.num_channels >> NB_ELEM_LOG2)) : + ((y_elem_cnt >> NB_ELEM_LOG2) >= (ctrl_i.tensor_size_m >> NB_ELEM_LOG2)); + assign last_n_tile = (n_tile_cnt >= (ctrl_i.tensor_size_n >> NB_ELEM_LOG2)); always_comb begin - write_to_buffer_done = 1'b0; - read_from_buffer_done = 1'b0; - if(matrix_tot_size != 0) begin + data_out_prefifo.strb = '1; + if(ctrl_i.total_elements != 0) begin if(ctrl_i.datamover_mode == DATAMOVER_COPY) begin // Copy mode data_out_prefifo.strb = ((tot_cnt_q >= total_accesses_copy_mode-1) && (remaining_elems != 0)) ? ((STRB_ONE << remaining_elems) - 1) : '1; - // write_to_buffer_done = (tot_cnt_q >= (matrix_tot_size >> NB_ELEM_LOG2) - 1); end else if(ctrl_i.datamover_mode == DATAMOVER_TRANSPOSE) begin // Transpose mode - if((last_m_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin - if(cnt_q >= leftover_rows) begin - write_to_buffer_done = (fsm_q == WRITE) ? 1'b1 : 1'b0; - read_from_buffer_done = (fsm_q == READ) ? 1'b1 : 1'b0; - end - if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin + if((last_y_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin + if((y_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); end else begin data_out_prefifo.strb = '0; end - end else if(last_m_tile && leftover_rows != 0) begin - if(cnt_q >= leftover_rows) begin - write_to_buffer_done = (fsm_q == WRITE) ? 1'b1 : 1'b0; - // read_from_buffer_done = (fsm_q == READ) ? 1'b1 : 1'b0; - end + end else if(last_y_tile && leftover_rows != 0) begin data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); end else if(last_n_tile && leftover_cols != 0) begin - if(cnt_q >= leftover_rows) begin - // write_to_buffer_done = (fsm_q == WRITE) ? 1'b1 : 1'b0; - read_from_buffer_done = (fsm_q == READ) ? 1'b1 : 1'b0; - end - if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin + if((y_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin data_out_prefifo.strb = '1; end else begin data_out_prefifo.strb = '0; @@ -213,14 +195,14 @@ module datamover_engine data_out_prefifo.strb = '1; end end else if(ctrl_i.datamover_mode == DATAMOVER_CIM_CONVERSION) begin // CIM layout conversion mode - if((last_m_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin - if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + if((last_y_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin + if((y_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin data_out_prefifo.strb = ((STRB_ONE << leftover_cols) - 1); end else begin data_out_prefifo.strb = '0; end - end else if(last_m_tile && leftover_rows != 0) begin - if((m_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + end else if(last_y_tile && leftover_rows != 0) begin + if((y_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin data_out_prefifo.strb = '1; end else begin data_out_prefifo.strb = '0; @@ -231,16 +213,16 @@ module datamover_engine data_out_prefifo.strb = '1; end end else if(ctrl_i.datamover_mode == DATAMOVER_UNFOLD) begin // Unfold mode - if((last_c_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin - if((c_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin + if((last_y_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin + if((y_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); end else begin data_out_prefifo.strb = '0; end - end else if(last_c_tile && leftover_rows != 0) begin + end else if(last_y_tile && leftover_rows != 0) begin data_out_prefifo.strb = ((STRB_ONE << leftover_rows) - 1); end else if(last_n_tile && leftover_cols != 0) begin - if((c_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin + if((y_elem_cnt & (NB_ELEMENTS - 1)) < leftover_cols) begin data_out_prefifo.strb = '1; end else begin data_out_prefifo.strb = '0; @@ -249,16 +231,16 @@ module datamover_engine data_out_prefifo.strb = '1; end end else if(ctrl_i.datamover_mode == DATAMOVER_FOLD) begin // Fold mode (inverse of unfold: leftover_rows <-> leftover_cols roles swapped) - if((last_c_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin - if((c_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + if((last_y_tile && leftover_rows != 0) && (last_n_tile && leftover_cols != 0)) begin + if((y_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin data_out_prefifo.strb = ((STRB_ONE << leftover_cols) - 1); end else begin data_out_prefifo.strb = '0; end end else if(last_n_tile && leftover_cols != 0) begin data_out_prefifo.strb = ((STRB_ONE << leftover_cols) - 1); - end else if(last_c_tile && leftover_rows != 0) begin - if((c_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin + end else if(last_y_tile && leftover_rows != 0) begin + if((y_elem_cnt & (NB_ELEMENTS - 1)) < leftover_rows) begin data_out_prefifo.strb = '1; end else begin data_out_prefifo.strb = '0; @@ -266,8 +248,6 @@ module datamover_engine end else begin data_out_prefifo.strb = '1; end - end else begin - data_out_prefifo.strb = '1; // ToDo: Could be set to 0 if all cases are handled properly end end end @@ -297,7 +277,6 @@ module datamover_engine end end assign cnt_d = (cnt_q < (ctrl_i.transp_len-ctrl_i.transp_stride)) ? cnt_q+ctrl_i.transp_stride : '0; - // assign cnt_d = ((write_to_buffer_done == 1'b0) || (cnt_q < (ctrl_i.transp_len-ctrl_i.transp_stride))) ? cnt_q+ctrl_i.transp_stride : '0; assign cnt_en = fsm_q == WRITE ? data_in_valid & data_in_ready : data_out_valid & data_out_ready; assign execution_done = (ctrl_i.datamover_mode == DATAMOVER_COPY) ? (total_accesses_copy_mode != 0) && (data_out_prefifo.valid & data_out_prefifo.ready) && (tot_cnt_q >= total_accesses_copy_mode - 1) : (total_accesses != 0) && (data_out_prefifo.valid & data_out_prefifo.ready) && (tot_cnt_q >= total_accesses - 1); @@ -308,8 +287,8 @@ module datamover_engine // "Smart shifting": this set of combinational blocks shifts data_in_unrolled // appropriately, depending on the configuration. // E.g., if you have a classical configuration with - // - NUM_ELEM_WORD = 4 and - // - ELEM_WIDTH = 8 bits, i.e. total is 32 bits per word + // - NUM_ELEM_WORD = 8 and + // - ELEM_WIDTH = 8 bits, i.e. total is 64 bits per word // the configurations are: 8b transpose, 16b transpose, 32b transpose. We assume // that transposes >= 64b can be done efficiently by Snitch processors through SSRs // and those < 8b are not interesting in our use case. @@ -390,25 +369,15 @@ module datamover_engine else $fatal("BANDWIDTH_ALIGNED (%0d) must be a multiple of WORD_WIDTH (%0d)", BANDWIDTH_ALIGNED, WORD_WIDTH); assert ((NB_ELEMENTS != 0) && ((NB_ELEMENTS & (NB_ELEMENTS - 1)) == 0)) else $fatal("NB_ELEMENTS (%0d) = BANDWIDTH_ALIGNED (%0d) / ELEM_WIDTH (%0d) must be a power of two", NB_ELEMENTS, BANDWIDTH_ALIGNED, ELEM_WIDTH); - assert (NUM_ELEM_WORD <= MAX_SHIFTING) // ToDo(cdurrer): obsolete? + assert (NUM_ELEM_WORD <= MAX_SHIFTING) else $fatal("NUM_ELEM_WORD (%0d) must not be greater than MAX_SHIFTING (%0d)", NUM_ELEM_WORD, MAX_SHIFTING); end // Runtime assertions - assert property (@(posedge clk_i) disable iff (!rst_ni) - ctrl_i.transp_len >= ctrl_i.transp_stride - ) else $error("transp_stride (%0d) must not exceed transp_len (%0d) — unsigned subtraction underflow", - ctrl_i.transp_stride, ctrl_i.transp_len); - - assert property (@(posedge clk_i) disable iff (!rst_ni) + assert property (@(posedge clk_i) disable iff (!rst_ni || $isunknown(ctrl_i.transp_len)) ctrl_i.transp_len <= NB_ELEMENTS ) else $error("transp_len (%0d) exceeds NB_ELEMENTS (%0d) — cnt_q will never match FSM transition condition", ctrl_i.transp_len, NB_ELEMENTS); - - assert property (@(posedge clk_i) disable iff (!rst_ni) - total_accesses >= (m_tiles * n_tiles * NB_ELEMENTS) - ) else $error("total_accesses overflow detected: m_tiles=%0d, n_tiles=%0d, NB_ELEMENTS=%0d", - m_tiles, n_tiles, NB_ELEMENTS); `endif `endif `endif diff --git a/rtl/datamover_package.sv b/rtl/datamover_package.sv index fbc6c8b..6674ebf 100644 --- a/rtl/datamover_package.sv +++ b/rtl/datamover_package.sv @@ -2,7 +2,7 @@ * datamover_package.sv * Francesco Conti * - * Copyright (C) 2019-2020 ETH Zurich, University of Bologna + * Copyright (C) 2019-2026 ETH Zurich, University of Bologna * Copyright and related rights are licensed under the Solderpad Hardware * License, Version 0.51 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at @@ -15,6 +15,7 @@ /* * Authors: Francesco Conti + * Cyrill Durrer */ package datamover_package; @@ -38,9 +39,9 @@ package datamover_package; logic [$clog2(MAX_BANDWIDTH/8):0] transp_len; logic [2:0] transp_stride; // 1, 2, or 4 elements datamover_mode_e datamover_mode; // 0: copy, 1: tranpose, 2: CIM layout conversion - logic [11:0] matrix_dim_m; - logic [11:0] matrix_dim_n; - logic [20:0] total_elements; // num_channels * dim_m * dim_n (pre-computed by HAL) + logic [11:0] tensor_size_m; + logic [11:0] tensor_size_n; + logic [20:0] total_elements; // num_channels * size_m * size_n (pre-computed by HAL) logic [10:0] num_channels; // number of channels (for unfolding/folding) } ctrl_engine_t; @@ -73,8 +74,8 @@ package datamover_package; parameter int unsigned DATAMOVER_REG_OUT_D2 = 32'h24; // [31:16] out_d2_stride; [15:0] out_d2_len parameter int unsigned DATAMOVER_REG_OUT_D3 = 32'h28; // [31:16] out_d3_stride; [15:0] out_d3_len parameter int unsigned DATAMOVER_REG_IN_OUT_D4_STRIDE = 32'h2C; // [31:16] out_d4_stride; [15:0] in_d4_stride (d4_len unnecessary due to tot_len) - parameter int unsigned DATAMOVER_REG_MATRIX_DIM = 32'h30; // [31:16] matrix_dim_n; [15:0] matrix_dim_m - parameter int unsigned DATAMOVER_REG_CHANNELS = 32'h34; // [31:11] total_elements = num_channels * dim_m * dim_n (pre-compute to save HW resources); [10:0] num_channels (for unfolding/folding) + parameter int unsigned DATAMOVER_REG_MATRIX_DIM = 32'h30; // [31:16] tensor_size_n; [15:0] tensor_size_m + parameter int unsigned DATAMOVER_REG_CHANNELS = 32'h34; // [31:11] total_elements = num_channels * size_m * size_n (pre-compute to save HW resources); [10:0] num_channels (for unfolding/folding) parameter int unsigned DATAMOVER_REG_CTRL_ENGINE = 32'h38; // [15:12] write_dim_en; [11:8] read_dim_en; [7:3] datamover_mode; [2:0] transp_mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) // Note: increase N_IO_REGS in datamover_top.sv when adding new registers here! diff --git a/rtl/datamover_streamer.sv b/rtl/datamover_streamer.sv index c3c62f3..43f6887 100644 --- a/rtl/datamover_streamer.sv +++ b/rtl/datamover_streamer.sv @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 ETH Zurich and University of Bologna + * Copyright (C) 2020-2026 ETH Zurich and University of Bologna * * Copyright and related rights are licensed under the Solderpad Hardware * License, Version 0.51 (the "License"); you may not use this file except in @@ -68,6 +68,10 @@ module datamover_streamer .IW ( IW ), .EW ( EW ), .EHW ( EHW) + // `ifndef SYNTHESIS + // ,.WAIVE_RQ4_ASSERT ( 1'b1 ) // ToDo: make sure that these waives are not hiding real issues in the design + // ,.WAIVE_RQ3_ASSERT ( 1'b1 ) + // `endif ) virt_tcdm [1:0] ( .clk ( clk_i ) ); @@ -168,7 +172,7 @@ module datamover_streamer .out ( tcdm_prefifo ) ); - // The HCI core FIFO the request path from the response path, easing + // The HCI core FIFO decouples the request path from the response path, easing // timing closure when integrating the accelerator in a cluster. hci_core_fifo #( .FIFO_DEPTH ( TCDM_FIFO_DEPTH ) diff --git a/rtl/datamover_top.sv b/rtl/datamover_top.sv index 2f024dc..3def209 100644 --- a/rtl/datamover_top.sv +++ b/rtl/datamover_top.sv @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 ETH Zurich and University of Bologna + * Copyright (C) 2020-2026 ETH Zurich and University of Bologna * * Copyright and related rights are licensed under the Solderpad Hardware * License, Version 0.51 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ /* * Authors: Francesco Conti * Sergio Mazzola + * Cyrill Durrer */ `include "hci_helpers.svh" @@ -23,12 +24,12 @@ module datamover_top import hci_package::*; import datamover_package::*; #( - parameter int unsigned ID = 4, // control slave peripheral ID width - ToDo(cdurrer): necessary? - parameter int unsigned BANDWIDTH = 288, // total bandwidth of HWPE to TCDM (in bits) - parameter int unsigned NUM_ELEM_WORD = 4, // number of elements in a memory bank word + parameter int unsigned ID = 4, // control slave peripheral ID width + parameter int unsigned BANDWIDTH = 512, // total bandwidth of HWPE to TCDM (in bits) + parameter int unsigned NUM_ELEM_WORD = 8, // number of elements in a memory bank word parameter int unsigned ELEM_WIDTH = 8, // element width (in bits) - parameter int unsigned N_CORES = 8, // number of cores for event inputs - parameter int unsigned N_CONTEXT = 4, // number of context for control slave regfile + parameter int unsigned N_CORES = 2, // number of cores for event inputs + parameter int unsigned N_CONTEXT = 2, // number of context for control slave regfile parameter int unsigned MISALIGNED_ACCESSES = 0, // enable misaligned accesses on TCDM interface parameter hci_size_parameter_t `HCI_SIZE_PARAM(tcdm) = '0, // Dependent parameters: do not modify! @@ -72,11 +73,6 @@ module datamover_top flags_slave_t slave_flags; ctrl_regfile_t reg_file; - // Data in and data out internal HWPE-Streams. Notice that the data width - // is set to 256 bits by default, 32 bits less than the default external - // bandwidth. The additional 32 bits of memory bandwidth are used to - // support access to non-word-aligned data packets. - // number of elements (in the full bandwidth, not a single bank word) localparam NB_ELEMENTS = BANDWIDTH_ALIGNED / ELEM_WIDTH; @@ -96,10 +92,10 @@ module datamover_top .clk(clk_i) ); - // The streamer exposes on the memory side a single TCDM 288-bit interface + // The streamer exposes on the memory side a single TCDM 512-bit interface // meant to be directly plugged into an Heterogeneous Cluster Interconnect. // On the accelerator side, it exposes an outgoing data in stream and - // an incoming data out HWPE-Streams, each 256-bit wide. + // an incoming data out HWPE-Streams, each 512-bit wide. datamover_streamer #( .BANDWIDTH ( BANDWIDTH ), .NUM_ELEM_WORD ( NUM_ELEM_WORD ), @@ -120,8 +116,9 @@ module datamover_top .flags_o ( streamer_flags ) ); - // The "engine", i.e., the datapath of the HWPE, is as simple as it gets: - // a FIFO copying the data in stream into the data out one! ToDo(cdurrer): update comment + // The engine transforms the data_in stream into data_out. Supported modes: + // copy, transpose, CIM layout conversion, unfold, and fold. + // An internal buffer (elem_matrix) of size BWxBW is used to reshuffle the data. datamover_engine #( .FIFO_DEPTH ( 4 ), .BANDWIDTH_ALIGNED ( BANDWIDTH_ALIGNED ), @@ -138,13 +135,7 @@ module datamover_top .data_out ( data_out ) ); - // The slave module exposes a peripheral interconnect HWPE-Periph plug; - // in the default configuration, it provides 4 contexts with 11 registers - // each, which are exposed into `reg_file.hwpe_params` - - // Previously it was 2 contexts with 13 registers: since the datamover is used for relatively - // fine-grained jobs, the new config makes offloading faster by compacting the LEN registers - // and allows for more contexts + // The slave module exposes a peripheral interconnect HWPE-Periph plug hwpe_ctrl_slave #( .REGFILE_SCM ( 0 ), .N_CORES ( N_CORES ), @@ -251,24 +242,17 @@ module datamover_top reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b001 ? 1 : reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][2:0] == 3'b010 ? 2 : 4; engine_ctrl.datamover_mode = datamover_mode_e'(reg_file.hwpe_params[DATAMOVER_REG_CTRL_ENGINE >> 2][7:3]); - engine_ctrl.matrix_dim_m = reg_file.hwpe_params[DATAMOVER_REG_MATRIX_DIM >> 2][15:0]; - engine_ctrl.matrix_dim_n = reg_file.hwpe_params[DATAMOVER_REG_MATRIX_DIM >> 2][31:16]; + engine_ctrl.tensor_size_m = reg_file.hwpe_params[DATAMOVER_REG_MATRIX_DIM >> 2][15:0]; + engine_ctrl.tensor_size_n = reg_file.hwpe_params[DATAMOVER_REG_MATRIX_DIM >> 2][31:16]; engine_ctrl.num_channels = reg_file.hwpe_params[DATAMOVER_REG_CHANNELS >> 2][10:0]; engine_ctrl.total_elements = reg_file.hwpe_params[DATAMOVER_REG_CHANNELS >> 2][31:11]; - engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; // ToDo(cdurrer): What exactly did transp_len do? Use it to handle leftovers? - // if(reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16] == '0) begin // no leftover - // engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; // ToDo(cdurrer): why? - // end - // else begin // in case of leftover, use the reg content as length - // engine_ctrl.transp_len = reg_file.hwpe_params[DATAMOVER_REG_TRANSP_MODE >> 2][31:16]; // ToDo(cdurrer): mention this option in config/pkg/TB - // end + engine_ctrl.transp_len = BANDWIDTH_ALIGNED/ELEM_WIDTH; end // Bind the output event, which is propagated to the event unit and used // to implement HWPE datamover barriers. assign evt_o = slave_flags.evt[N_CORES-1:0]; - localparam int unsigned DEBUG_DW = `HCI_SIZE_GET_DW(tcdm); localparam int unsigned DEBUG_BW = `HCI_SIZE_GET_BW(tcdm); localparam int unsigned DEBUG_AW = `HCI_SIZE_GET_AW(tcdm); diff --git a/rtl/datamover_top_wrap.sv b/rtl/datamover_top_wrap.sv index 0024ff3..eec1a6f 100644 --- a/rtl/datamover_top_wrap.sv +++ b/rtl/datamover_top_wrap.sv @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 ETH Zurich and University of Bologna + * Copyright (C) 2025-2026 ETH Zurich and University of Bologna * * Copyright and related rights are licensed under the Solderpad Hardware * License, Version 0.51 (the "License"); you may not use this file except in @@ -13,7 +13,7 @@ /* * Authors: Sergio Mazzola - * Arpan Suravi Prasad s + * Arpan Suravi Prasad */ // A wrapper for datamover_top that unrolls the interfaces to HCI and @@ -33,7 +33,7 @@ module datamover_top_wrap parameter bit WAIVE_RSP3_ASSERT = 1'b0, parameter bit WAIVE_RSP5_ASSERT = 1'b0, `endif - parameter int unsigned ADDR_WIDTH = 32, // width of addres bus + parameter int unsigned ADDR_WIDTH = 32, // width of address bus parameter int unsigned ID = 10, // control slave peripheral ID width parameter int unsigned BANDWIDTH = 288, // total bandwidth of HWPE to TCDM (in bits) parameter int unsigned NUM_ELEM_WORD = 4, // number of elements in a memory bank word @@ -112,13 +112,13 @@ module datamover_top_wrap // All banks are accessed at the same time, so `req` and `wen` are the same for all banks assign tcdm_req[i] = tcdm.req; assign tcdm_wen[i] = tcdm.wen; - // The datamover accessess a number of NUM_WORDS adjacent words. + // The datamover accesses a number of NUM_WORDS adjacent words. // Assuming the memory is element-indexed, i.e., every word-element is individually addressable // on the address bus: to go from word `n` to word `n+1` we have to skip NUM_ELEM_WORD addresses. assign tcdm_add[i] = tcdm.add + i * NUM_ELEM_WORD; assign tcdm_be[i] = tcdm.be[(i+1)*NUM_ELEM_WORD - 1 : i*NUM_ELEM_WORD]; assign tcdm_data[i] = tcdm.data[(i+1)*WORD_WIDTH - 1 : i*WORD_WIDTH]; - end + end assign tcdm.gnt = &(tcdm_gnt); // only when all words are granted assign tcdm.r_data = { >> {tcdm_r_data}}; assign tcdm.r_valid = &(tcdm_r_valid); // only when all words are valid @@ -190,4 +190,4 @@ module datamover_top_wrap `endif `endif -endmodule // datamover_top_wrap \ No newline at end of file +endmodule // datamover_top_wrap diff --git a/test/datamover_test.c b/test/datamover_test.c new file mode 100644 index 0000000..958412c --- /dev/null +++ b/test/datamover_test.c @@ -0,0 +1,193 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Authors: Cyrill Durrer +// Daniel Keller +// Sergio Mazzola + +#include +#include +#include + +#include "snrt.h" +#include "konark_cluster_raw_addrmap.h" + +#include "konark/hal_datamover.h" +#include "konark/hal_konark.h" + +#include "data.h" + +#define VERBOSE 1 +#if VERBOSE +#include "printf.h" +#endif + +#define P 4 // Unfold patch size (number of elements in a patch) for datamover_unfold + +static inline int datamover_compare_int (uint64_t *actual, uint64_t *golden, int total_bytes) { + int errors = 0; + int len = (int)(total_bytes / 8); // Number of complete uint64_t words + int remaining_elements = total_bytes % 8; + + printf("[DM-INFO] Comparing %d 64b-words + %d 8b-elements:\n", len, remaining_elements); + for (int i=0; i 0) { + for (int i=0; i 0) return 0; + + const uint32_t TOT_SIZE = SIZE_C * SIZE_M * SIZE_N; + datamover_status_t datamover_status; + int errors = 0; + datamover_transp_mode_t transp_mode = DATAMOVER_TRANSP_NONE; + + switch (TRANSP_MODE) { + case 1: + transp_mode = DATAMOVER_TRANSP_1ELEM; + break; + case 2: + transp_mode = DATAMOVER_TRANSP_2ELEM; + break; + case 4: + transp_mode = DATAMOVER_TRANSP_4ELEM; + break; + default: + if (DATAMOVER_MODE == 1 || DATAMOVER_MODE == 3) { + printf("[DM-ERR] Unknown DATAMOVER_TRANSPOSE_MODE=%d: Must be 1, 2, or 4\n", TRANSP_MODE); + return -1; + } + } + + #if VERBOSE + printf("[INFO] cluster = %u, core(cluster) = %u, core(global) = %u\n", snrt_cluster_idx(), snrt_cluster_core_idx(), snrt_global_core_idx()); + #endif + + // Allocate and load buffers on DMA core + static uint8_t *local_in; + static uint8_t *local_out; + static uint8_t *local_gold; + if (snrt_is_dm_core()) { + local_in = (uint8_t *) snrt_l1_alloc_cluster_local(TOT_SIZE, 8); // Alignment paramter: 64: bank-aligned, 8: word-aligned + local_out = (uint8_t *) snrt_l1_alloc_cluster_local(TOT_SIZE, 8); + local_gold = (uint8_t *) snrt_l1_alloc_cluster_local(TOT_SIZE, 8); + + printf("[DM-INFO] Allocated L1 buffers: local_in=%p, local_out=%p, local_gold=%p\n", local_in, local_out, local_gold); + + // Move DMA input and expected goldens into TCDM + snrt_dma_start_1d(local_in, golden_in, TOT_SIZE); + snrt_dma_start_1d(local_gold, golden_out, TOT_SIZE); + snrt_dma_wait_all(); + + // Initialize output buffer with dummy value + for(int i=0; iCIM-layout) or 1 (CIM-layout->CHW)\n", CIM_MODE); + } + break; + case 3: + printf("[DM-INFO] Starting Datamover CIM LAYOUT + TRANSPOSE (%u elements) operation of %ux%u (CONVERTED DIMENSIONS) tensor (CIM inner dimension = %u)\n", transp_mode, SIZE_M, SIZE_N, ROW_TILE_SIZE); + datamover_status = datamover_cim_layout_transpose_blocking(local_in, local_out, SIZE_M, SIZE_N, ROW_TILE_SIZE, transp_mode, timeout); + break; + case 4: + printf("[DM-INFO] Starting Datamover UNFOLD operation of %ux%ux%u tensor (CHW) (unfolded dimension = %ux%ux%u)\n", SIZE_C, SIZE_M, SIZE_N, P, SIZE_M*SIZE_N / P, SIZE_C); + datamover_status = datamover_unfold_blocking(local_in, local_out, SIZE_C, SIZE_M, SIZE_N, timeout); + break; + case 5: + printf("[DM-INFO] Starting Datamover FOLD operation of %ux%ux%u tensor (unfolded PNC) (folded dimension = %ux%ux%u)\n", P, SIZE_M*SIZE_N / P, SIZE_C, SIZE_C, SIZE_M, SIZE_N); + datamover_status = datamover_fold_blocking(local_in, local_out, SIZE_C, SIZE_M, SIZE_N, timeout); + break; + default: + printf("[DM-ERR] Unknown DATAMOVER_MODE=%d: Must be 0 (COPY), 1 (TRANSPOSE), 2 (CIM LAYOUT), 3 (CIM LAYOUT TRANSPOSE), 4 (UNFOLD), or 5 (FOLD)\n", DATAMOVER_MODE); + return -1; + } + + // Disable all HWPE clocks + konark_hwpe_disable_all_clk(); + + // Verification + #if VERBOSE + printf("[INFO] Verifying result...\n"); + #endif + errors = datamover_compare_int((uint64_t*)local_out, (uint64_t*)local_gold, TOT_SIZE); + #if VERBOSE + if (errors == 0) { + printf("[DM-OK] ======= DATAMOVER TEST PASSED =======\n"); + printf(" *** *** \n"); + printf(" ** ** ** ** \n"); + printf(" *** *** \n"); + printf(" /_ \n"); + printf(" , , \n"); + printf(" '-......-' \n\n"); + } else { + printf("[DM-ERR] !!!!!!! DATAMOVER TEST FAILED !!!!!!!\n"); + printf("[DM-ERR] mismatches: %d (%ux%ux%u tensor)\n", errors, SIZE_C, SIZE_M, SIZE_N); + } + #endif + } + + return errors; +} diff --git a/test/hal_datamover.c b/test/hal_datamover.c new file mode 100644 index 0000000..c650663 --- /dev/null +++ b/test/hal_datamover.c @@ -0,0 +1,551 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Authors: Sergio Mazzola +// Cyrill Durrer +// Daniel Keller +// Francesco Conti + +#include +#include + +#include "konark/hal_datamover.h" +#include "konark/hal_hwpe.h" + +///////////// +// Drivers // +///////////// + +/* HWPE mandatory registers */ + +int datamover_acquire_task(void) { + return hwpe_task_queue_acquire_task(DATAMOVER_BASE_ADDR); +} + +void datamover_trigger_task(void) { + hwpe_task_queue_release_and_run(DATAMOVER_BASE_ADDR); +} + +uint32_t datamover_finished(void) { + return hwpe_finished(DATAMOVER_BASE_ADDR); +} + +uint32_t datamover_get_status(void) { + return hwpe_task_queue_status(DATAMOVER_BASE_ADDR); +} + +void datamover_soft_clear(void) { + hwpe_soft_clear(DATAMOVER_BASE_ADDR); +} + +/* Datamover-specific registers */ +//TODO(smazzola): For now only usage of context 0 is supported + +void datamover_in_set(uint32_t value) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_IN_PTR_OFFSET, value); +} + +void datamover_out_set(uint32_t value) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_OUT_PTR_OFFSET, value); +} + +void datamover_tot_len_set(uint32_t value) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_TOT_LEN_OFFSET, value); +} + +void datamover_in_d0_set(uint32_t stride, uint32_t len) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_IN_D0_OFFSET, ((stride & 0xFFFF) << 16) | (len & 0xFFFF)); +} + +void datamover_in_d1_set(uint32_t stride, uint32_t len) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_IN_D1_OFFSET, ((stride & 0xFFFF) << 16) | (len & 0xFFFF)); +} + +void datamover_in_d2_set(uint32_t stride, uint32_t len) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_IN_D2_OFFSET, ((stride & 0xFFFF) << 16) | (len & 0xFFFF)); +} + +void datamover_in_d3_set(uint32_t stride, uint32_t len) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_IN_D3_OFFSET, ((stride & 0xFFFF) << 16) | (len & 0xFFFF)); +} + +void datamover_out_d0_set(uint32_t stride, uint32_t len) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_OUT_D0_OFFSET, ((stride & 0xFFFF) << 16) | (len & 0xFFFF)); +} + +void datamover_out_d1_set(uint32_t stride, uint32_t len) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_OUT_D1_OFFSET, ((stride & 0xFFFF) << 16) | (len & 0xFFFF)); +} + +void datamover_out_d2_set(uint32_t stride, uint32_t len) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_OUT_D2_OFFSET, ((stride & 0xFFFF) << 16) | (len & 0xFFFF)); +} + +void datamover_out_d3_set(uint32_t stride, uint32_t len) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_OUT_D3_OFFSET, ((stride & 0xFFFF) << 16) | (len & 0xFFFF)); +} + +void datamover_in_out_d4_stride_set(uint32_t out_stride, uint32_t in_stride) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_IN_OUT_D4_STRIDE_OFFSET, ((out_stride & 0xFFFF) << 16) | (in_stride & 0xFFFF)); +} + +void datamover_matrix_dim_set(uint32_t tensor_size_n, uint32_t tensor_size_m) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_MATRIX_DIM_OFFSET, ((tensor_size_n & 0xFFFF) << 16) | (tensor_size_m & 0xFFFF)); +} + +void datamover_channels_set(uint32_t total_elements, uint32_t num_channels) { + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_CHANNELS_OFFSET, ((total_elements & 0x1FFFFF) << 11) | (num_channels & 0x7FF)); +} + +void datamover_ctrl_engine_set(datamover_mode_t datamover_mode, uint32_t write_dim_en, uint32_t read_dim_en, datamover_transp_mode_t transp_mode) { // dim_en: bit-mask (bit0=d1, bit1=d2, bit2=d3, bit3=d4) + DATAMOVER_HWPE_REG_WRITE(DATAMOVER_REGISTER_OFFSET + DATAMOVER_REG_CTRL_ENGINE_OFFSET, ((write_dim_en & 0xF) << 12) | ((read_dim_en & 0xF) << 8) | ((datamover_mode & 0x1F) << 3) | (transp_mode & 0x7)); +} + +///////// +// HAL // +///////// + +datamover_status_t datamover_wait_done(uint64_t timeout) { + int status = 0; + int finished = 0; + + // Wait for all jobs or timeout expiration (NOTE: FINISHED register can behave unexpectedly) + // do { + // finished = datamover_finished(); + // } while (finished == 0 && --timeout); + + // Wait for all jobs or timeout expiration + do { + status = datamover_get_status(); // ToDo: Why STATUS register and not FINISHED register? + } while (status != 0 && --timeout); + + if (timeout == 0) { + #if VERBOSE + printf("[ERROR] datamover_wait_done(): Timeout expired, jobs are stuck.\n"); + #endif + return DATAMOVER_TO; + } + #if VERBOSE + printf("[DM-HAL] datamover_wait_done: Job(s) finished successfully.\n"); + #endif + return DATAMOVER_OK; +} + +datamover_status_t datamover_copy(uint8_t *src, uint8_t *dst, uint32_t size_m, uint32_t size_n) { + int acq_to = 1000000; + int job_id = -1; + uint32_t total_accesses = (size_m * size_n) / DATAMOVER_BANDWIDTH_ELEMS; // floored division + + if ((size_m * size_n) % DATAMOVER_BANDWIDTH_ELEMS != 0) { + total_accesses += 1; // additional access for remaining elements + } + #if VERBOSE + printf("[DM-HAL] datamover_copy: size_m=%u, size_n=%u, total_elements=%u, BANDWIDTH_ELEMS=%u, total_accesses=%u\n", size_m, size_n, size_m * size_n, DATAMOVER_BANDWIDTH_ELEMS, total_accesses); + #endif + + while ((job_id = datamover_acquire_task()) < 0 && --acq_to) {} + if (acq_to == 0) { + return DATAMOVER_TO; + } + + datamover_in_set((uint32_t)src); + datamover_out_set((uint32_t)dst); + datamover_tot_len_set(total_accesses); + datamover_in_d0_set(DATAMOVER_BANDWIDTH_ELEMS, total_accesses); + datamover_in_d1_set(0, 0); + datamover_in_d2_set(0, 0); + datamover_in_d3_set(0, 0); + datamover_out_d0_set(DATAMOVER_BANDWIDTH_ELEMS, total_accesses); + datamover_out_d1_set(0, 0); + datamover_out_d2_set(0, 0); + datamover_out_d3_set(0, 0); + datamover_in_out_d4_stride_set(0, 0); + datamover_matrix_dim_set(size_n, size_m); + datamover_channels_set(size_m * size_n, 1); + datamover_ctrl_engine_set(DATAMOVER_COPY, 0, 0, DATAMOVER_TRANSP_NONE); + + datamover_trigger_task(); + + return DATAMOVER_OK; +} + +datamover_status_t datamover_copy_blocking(uint8_t *src, uint8_t *dst, uint32_t size_m, uint32_t size_n, uint64_t timeout) { + datamover_status_t status; + status = datamover_copy(src, dst, size_m, size_n); + if (status != DATAMOVER_OK) { + return status; + } + status = datamover_wait_done(timeout); + return status; +} + +datamover_status_t datamover_transpose(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, datamover_transp_mode_t transp_mode) { + int acq_to = 1000000; + int job_id = -1; + + int m_tiles = size_m % DATAMOVER_BANDWIDTH_ELEMS ? (size_m / DATAMOVER_BANDWIDTH_ELEMS) + 1 : (size_m / DATAMOVER_BANDWIDTH_ELEMS); // number of tiles in M dimension (rounded up) + int n_tiles = size_n % DATAMOVER_BANDWIDTH_ELEMS ? (size_n / DATAMOVER_BANDWIDTH_ELEMS) + 1 : (size_n / DATAMOVER_BANDWIDTH_ELEMS); // number of tiles in N dimension (rounded up) + + while ((job_id = datamover_acquire_task()) < 0 && --acq_to) {} + if (acq_to == 0) { + return DATAMOVER_TO; + } + + datamover_in_set((uint32_t)matrix_in); + datamover_out_set((uint32_t)matrix_out); + datamover_tot_len_set(m_tiles * n_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d0_set(size_n, m_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d1_set(DATAMOVER_BANDWIDTH_ELEMS, n_tiles); + datamover_in_d2_set(0, 0); + datamover_in_d3_set(0, 0); + datamover_out_d0_set(size_m * transp_mode, DATAMOVER_BANDWIDTH_ELEMS / transp_mode); + datamover_out_d1_set(DATAMOVER_BANDWIDTH_ELEMS, m_tiles * transp_mode); + datamover_out_d2_set(size_m * DATAMOVER_BANDWIDTH_ELEMS, 0); + datamover_out_d3_set(0, 0); + datamover_in_out_d4_stride_set(0, 0); + datamover_matrix_dim_set(size_n, size_m); + datamover_channels_set(size_m * size_n, 1); + datamover_ctrl_engine_set(DATAMOVER_TRANSP, 0x3, 0x1, transp_mode); + + datamover_trigger_task(); + + return DATAMOVER_OK; +} + +datamover_status_t datamover_transpose_blocking(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, datamover_transp_mode_t transp_mode, uint64_t timeout) { + datamover_status_t status; + + status = datamover_transpose(matrix_in, matrix_out, size_m, size_n, transp_mode); + if (status != DATAMOVER_OK) { + return status; + } + + status = datamover_wait_done(timeout); + return status; +} + +void datamover_cim_layout_config_complete_tiles(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, uint32_t row_tile_size) { // Configure datamover for complete tiles in N dimension + uint32_t m_tiles = size_m % DATAMOVER_BANDWIDTH_ELEMS ? (size_m / DATAMOVER_BANDWIDTH_ELEMS) + 1 : (size_m / DATAMOVER_BANDWIDTH_ELEMS); // number of tiles in M dimension (rounded up) + uint32_t complete_n_tiles = size_n / row_tile_size; // number of complete tiles in N dimension + + datamover_in_set((uint32_t)matrix_in); + datamover_out_set((uint32_t)matrix_out); + datamover_tot_len_set(m_tiles * complete_n_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d0_set(DATAMOVER_BANDWIDTH_ELEMS, row_tile_size / DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d1_set(size_n, m_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d2_set(row_tile_size, 0); + datamover_in_d3_set(0, 0); + datamover_out_d0_set(DATAMOVER_BANDWIDTH_ELEMS, m_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_out_d1_set(row_tile_size * size_m, complete_n_tiles); + datamover_out_d2_set(0, 0); + datamover_out_d3_set(0, 0); + datamover_in_out_d4_stride_set(0, 0); + datamover_matrix_dim_set(complete_n_tiles * DATAMOVER_BANDWIDTH_ELEMS, size_m); + datamover_channels_set(complete_n_tiles * DATAMOVER_BANDWIDTH_ELEMS * size_m, 1); + datamover_ctrl_engine_set(DATAMOVER_CIM_LAYOUT, 0x1, 0x3, DATAMOVER_TRANSP_NONE); +} + +void datamover_cim_layout_config_leftover_tiles(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, uint32_t row_tile_size) { // Configure datamover for leftover tiles in N dimension + uint32_t m_tiles = size_m % DATAMOVER_BANDWIDTH_ELEMS ? (size_m / DATAMOVER_BANDWIDTH_ELEMS) + 1 : (size_m / DATAMOVER_BANDWIDTH_ELEMS); // number of tiles in M dimension (rounded up) + uint32_t complete_n_tiles = size_n / row_tile_size; // number of complete tiles in N dimension + uint32_t leftover_columns = size_n % DATAMOVER_BANDWIDTH_ELEMS; + uint8_t *matrix_in_shifted = matrix_in + complete_n_tiles * DATAMOVER_BANDWIDTH_ELEMS; + uint8_t *matrix_out_shifted = matrix_out + complete_n_tiles * size_m * DATAMOVER_BANDWIDTH_ELEMS; + + datamover_in_set((uint32_t)matrix_in_shifted); + datamover_out_set((uint32_t)matrix_out_shifted); + datamover_tot_len_set(m_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d0_set(DATAMOVER_BANDWIDTH_ELEMS, row_tile_size / DATAMOVER_BANDWIDTH_ELEMS); // Unused if row_tile_size == BANDWIDTH_ELEMS + datamover_in_d1_set(size_n, m_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d2_set(0, 0); + datamover_in_d3_set(0, 0); + datamover_out_d0_set(leftover_columns, m_tiles * DATAMOVER_BANDWIDTH_ELEMS); // different stride for leftover columns + datamover_out_d1_set(0, 0); + datamover_out_d2_set(0, 0); + datamover_out_d3_set(0, 0); + datamover_in_out_d4_stride_set(0, 0); + datamover_matrix_dim_set(leftover_columns, size_m); + datamover_channels_set(leftover_columns * size_m, 1); + datamover_ctrl_engine_set(DATAMOVER_CIM_LAYOUT, 0x0, 0x1, DATAMOVER_TRANSP_NONE); +} + +datamover_status_t datamover_cim_layout(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, uint32_t row_tile_size) { + // For row-major to A-layout: row_tile_size = CIM inner dimension [elements] (64) + // For row-major to B-layout: row_tile_size = CIM outer dimension [elements] (8x #CIM) + // NOTE: Only supports row_tile_size = BANDWIDTH_ELEMS + // ToDo: Currently BLOCKING for misaligned matrices (waiting for completion of complete tiles before handling leftovers) + int acq_to = 1000000; + int job_id = -1; + + uint32_t complete_n_tiles = size_n / row_tile_size; // number of complete tiles in N dimension + + uint32_t leftover_columns = size_n % DATAMOVER_BANDWIDTH_ELEMS; + if(leftover_columns != 0) { // Misaligned matrix: Handle complete tiles first, then leftover columns + if(size_n > DATAMOVER_BANDWIDTH_ELEMS) { // Handle complete tiles + while ((job_id = datamover_acquire_task()) < 0 && --acq_to) {} + if (acq_to == 0) { + return DATAMOVER_TO; + } + datamover_cim_layout_config_complete_tiles(matrix_in, matrix_out, size_m, size_n, row_tile_size); + datamover_trigger_task(); + } // Handle leftover columns + while ((job_id = datamover_acquire_task()) < 0 && --acq_to) {} + if (acq_to == 0) { + return DATAMOVER_TO; + } + datamover_status_t wait_status = datamover_wait_done(5000000); // ToDo: use second context instead of waiting for completion and reconfiguring datamover for leftover columns + if (wait_status != DATAMOVER_OK) { + return wait_status; + } + + datamover_cim_layout_config_leftover_tiles(matrix_in, matrix_out, size_m, size_n, row_tile_size); + datamover_trigger_task(); + } + else { // Aligned matrix: Handle complete tiles only + while ((job_id = datamover_acquire_task()) < 0 && --acq_to) {} + if (acq_to == 0) { + return DATAMOVER_TO; + } + datamover_cim_layout_config_complete_tiles(matrix_in, matrix_out, size_m, size_n, row_tile_size); + datamover_trigger_task(); + } + return DATAMOVER_OK; +} + +datamover_status_t datamover_cim_layout_blocking(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, uint32_t row_tile_size, uint64_t timeout) { + datamover_status_t status; + + status = datamover_cim_layout(matrix_in, matrix_out, size_m, size_n, row_tile_size); + if (status != DATAMOVER_OK) { + return status; + } + + status = datamover_wait_done(timeout); + return status; +} + +void datamover_cim_layout_reverse_config_complete_tiles(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, uint32_t row_tile_size) { // Configure datamover for complete tiles in N dimension + uint32_t complete_n_tiles = size_n / row_tile_size; // number of complete tiles in N dimension + uint32_t cim_layout_m_tiles = (size_m * complete_n_tiles) % DATAMOVER_BANDWIDTH_ELEMS ? ((size_m * complete_n_tiles) / DATAMOVER_BANDWIDTH_ELEMS) + 1 : (size_m * complete_n_tiles) / DATAMOVER_BANDWIDTH_ELEMS; // number of tiles in M dimension (rounded up) + + datamover_in_set((uint32_t)matrix_in); + datamover_out_set((uint32_t)matrix_out); + datamover_tot_len_set(cim_layout_m_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d0_set(DATAMOVER_BANDWIDTH_ELEMS, size_m * complete_n_tiles); + datamover_in_d1_set(0, 0); + datamover_in_d2_set(0, 0); + datamover_in_d3_set(0, 0); + datamover_out_d0_set(size_n, size_m); + datamover_out_d1_set(DATAMOVER_BANDWIDTH_ELEMS, complete_n_tiles); + datamover_out_d2_set(0, 0); + datamover_out_d3_set(0, 0); + datamover_in_out_d4_stride_set(0, 0); + datamover_matrix_dim_set(DATAMOVER_BANDWIDTH_ELEMS, size_m * complete_n_tiles); // Representing CIM layout instead of original dimensions + datamover_channels_set(DATAMOVER_BANDWIDTH_ELEMS * size_m * complete_n_tiles, 1); + datamover_ctrl_engine_set(DATAMOVER_CIM_LAYOUT, 0x1, 0x0, DATAMOVER_TRANSP_NONE); +} + +void datamover_cim_layout_reverse_config_leftover_tiles(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, uint32_t row_tile_size) { // Configure datamover for leftover tiles in N dimension + uint32_t m_tiles = size_m % DATAMOVER_BANDWIDTH_ELEMS ? (size_m / DATAMOVER_BANDWIDTH_ELEMS) + 1 : (size_m / DATAMOVER_BANDWIDTH_ELEMS); // number of tiles in M dimension (rounded up) + uint32_t complete_n_tiles = size_n / row_tile_size; // number of complete tiles in N dimension + uint32_t leftover_columns = size_n % DATAMOVER_BANDWIDTH_ELEMS; + uint8_t *matrix_in_shifted = matrix_in + complete_n_tiles * size_m * DATAMOVER_BANDWIDTH_ELEMS; + uint8_t *matrix_out_shifted = matrix_out + complete_n_tiles * DATAMOVER_BANDWIDTH_ELEMS; + + datamover_in_set((uint32_t)matrix_in_shifted); + datamover_out_set((uint32_t)matrix_out_shifted); + datamover_tot_len_set(m_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d0_set(leftover_columns, m_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d1_set(0, 0); + datamover_in_d2_set(0, 0); + datamover_in_d3_set(0, 0); + datamover_out_d0_set(size_n, m_tiles * DATAMOVER_BANDWIDTH_ELEMS); // different stride for leftover columns + datamover_out_d1_set(0, 0); + datamover_out_d2_set(0, 0); + datamover_out_d3_set(0, 0); + datamover_in_out_d4_stride_set(0, 0); + datamover_matrix_dim_set(leftover_columns, size_m); + datamover_channels_set(leftover_columns * size_m, 1); + datamover_ctrl_engine_set(DATAMOVER_CIM_LAYOUT, 0x0, 0x0, DATAMOVER_TRANSP_NONE); +} + +datamover_status_t datamover_cim_layout_reverse(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, uint32_t row_tile_size) { + // For A-layout to row-major: row_tile_size = CIM inner dimension [elements] (64) + // For B-layout to row-major: row_tile_size = CIM outer dimension [elements] (8x #CIM) + // NOTE: Only supports row_tile_size = BANDWIDTH_ELEMS for now + int acq_to = 1000000; + int job_id = -1; + + uint32_t complete_n_tiles = size_n / row_tile_size; // number of complete tiles in N dimension + + // Handle leftover columns (if any) + uint32_t leftover_columns = size_n % DATAMOVER_BANDWIDTH_ELEMS; + if(leftover_columns != 0) { + if(size_n > DATAMOVER_BANDWIDTH_ELEMS) { // Handle complete tiles + while ((job_id = datamover_acquire_task()) < 0 && --acq_to) {} + if (acq_to == 0) { + return DATAMOVER_TO; + } + datamover_cim_layout_reverse_config_complete_tiles(matrix_in, matrix_out, size_m, size_n, row_tile_size); + datamover_trigger_task(); + } + // Handle leftover columns + while ((job_id = datamover_acquire_task()) < 0 && --acq_to) {} + if (acq_to == 0) { + return DATAMOVER_TO; + } + datamover_status_t wait_status = datamover_wait_done(5000000); // ToDo: use second context instead of waiting for completion and reconfiguring datamover for leftover columns + if (wait_status != DATAMOVER_OK) { + return wait_status; + } + + datamover_cim_layout_reverse_config_leftover_tiles(matrix_in, matrix_out, size_m, size_n, row_tile_size); + datamover_trigger_task(); + } + else { // Aligned matrix: Handle complete tiles only + while ((job_id = datamover_acquire_task()) < 0 && --acq_to) {} + if (acq_to == 0) { + return DATAMOVER_TO; + } + datamover_cim_layout_reverse_config_complete_tiles(matrix_in, matrix_out, size_m, size_n, row_tile_size); + datamover_trigger_task(); + } + return DATAMOVER_OK; +} + +datamover_status_t datamover_cim_layout_reverse_blocking(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, uint32_t row_tile_size, uint64_t timeout) { + datamover_status_t status; + status = datamover_cim_layout_reverse(matrix_in, matrix_out, size_m, size_n, row_tile_size); + if (status != DATAMOVER_OK) { + return status; + } + + status = datamover_wait_done(timeout); + return status; +} + +datamover_status_t datamover_cim_layout_transpose_blocking(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_m, uint32_t size_n, uint32_t row_tile_size, datamover_transp_mode_t transp_mode, uint64_t timeout) { + // Performs transposition of a matrix in CIM layout, with input and output in CIM layout, by internally performing the necessary layout conversions and transposition in row-major layout. + // DATAMOVER_MODE = 3 is not passed to the HW! It is currently a placeholder for an optimized implementation. + // IMPORTANT: This function uses the input buffer as temporary storage for the transposed matrix in row-major layout. THE ORIGINAL CONTENT OF THE INPUT BUFFER WILL BE OVERWRITTEN! + // ToDo: Implement non-blocking version + datamover_status_t status; + if (size_n <= row_tile_size && size_m <= row_tile_size) { + // If the matrix has only one tile in N and M dimensions, no need to perform 3 separate operations, because CIM layout is the same as row-major layout + #if VERBOSE + printf("[DM-INFO] Single tile in N and M dimensions, performing direct transpose from CIM-layout to row-major and vice versa, %ux%u matrix\n", size_n, size_m); + #endif + status = datamover_transpose_blocking(matrix_in, matrix_out, size_m, size_n, transp_mode, timeout); + } // ToDo(optional): Add special handling for matrices with only one tile in N dimension (size_n <= row_tile_size) but multiple tiles in M dimension, and the opposite case, omitting the unnecessary layout conversion. Requires either an additional memory space or a copy operation. + else { + // Transposition with input and output in CIM layout: 3-phase execution: 1) CIM-layout to row-major, 2) Transpose in row-major, 3) Row-major to CIM-layout + #if VERBOSE + printf("[DM-INFO] OPERATION 1: CIM-layout to row-major, %ux%u matrix\n", (size_n/row_tile_size), (size_m*row_tile_size)); + #endif + status = datamover_cim_layout_reverse_blocking(matrix_in, matrix_out, size_m, size_n, row_tile_size, timeout); + if (status != DATAMOVER_OK) return status; + #if VERBOSE + printf("[DM-INFO] OPERATION 2: Transpose of %ux%u matrix\n", size_m, size_n); + #endif + status = datamover_transpose_blocking(matrix_out, matrix_in, size_m, size_n, transp_mode, timeout); + if (status != DATAMOVER_OK) return status; + #if VERBOSE + printf("[DM-INFO] OPERATION 3: Row-major to CIM-layout, %ux%u matrix\n", size_n, size_m); + #endif + status = datamover_cim_layout_blocking(matrix_in, matrix_out, size_n, size_m, row_tile_size, timeout); + } + return status; +} + +datamover_status_t datamover_unfold(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_c, uint32_t size_h, uint32_t size_w) { + // Converts tensor (C,H,W) to unfolded tensor (P,N,C), patch size P = 2x2 = 4 (other patch dimensions not supported yet) + // matrix_in: input tensor in (C,H,W) layout + // matrix_out: output tensor in unfolded (P,N=(H*W)/P,C) layout + // size_c, size_h, size_w: dimensions of the input tensor + const int P = 4; // Patch size (number of elements in a patch), only tested for P=4 (2x2) + const int side_P = 2; // Patch sidelength + int acq_to = 1000000; + int job_id = -1; + uint32_t c_tiles = (size_c + DATAMOVER_BANDWIDTH_ELEMS - 1) / DATAMOVER_BANDWIDTH_ELEMS; // number of tiles in C dimension (rounded up) + uint32_t w_tiles = (size_w + DATAMOVER_BANDWIDTH_ELEMS - 1) / DATAMOVER_BANDWIDTH_ELEMS; // number of tiles in W dimension (rounded up) + + while ((job_id = datamover_acquire_task()) < 0 && --acq_to) {} + if (acq_to == 0) { + return DATAMOVER_TO; + } + datamover_in_set((uint32_t)matrix_in); + datamover_out_set((uint32_t)matrix_out); + datamover_tot_len_set(c_tiles * w_tiles * DATAMOVER_BANDWIDTH_ELEMS * size_h); + datamover_in_d0_set(size_h * size_w, c_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_in_d1_set(DATAMOVER_BANDWIDTH_ELEMS, w_tiles); + datamover_in_d2_set(size_w, size_h); + datamover_in_d3_set(0, 0); + datamover_out_d0_set(size_c * size_h * size_w / P, side_P); + datamover_out_d1_set(size_c, (w_tiles * DATAMOVER_BANDWIDTH_ELEMS) / side_P); + datamover_out_d2_set(DATAMOVER_BANDWIDTH_ELEMS, c_tiles); + datamover_out_d3_set(size_c * size_h * size_w / side_P, side_P); + datamover_in_out_d4_stride_set(size_c * size_w / side_P, 0); + datamover_matrix_dim_set(size_w, size_h); + datamover_channels_set(size_c * size_h * size_w, size_c); + datamover_ctrl_engine_set(DATAMOVER_UNFOLD, 0xF, 0x3, DATAMOVER_TRANSP_1ELEM); + + datamover_trigger_task(); + return DATAMOVER_OK; +} + +datamover_status_t datamover_unfold_blocking(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_c, uint32_t size_h, uint32_t size_w, uint64_t timeout) { + datamover_status_t status; + status = datamover_unfold(matrix_in, matrix_out, size_c, size_h, size_w); + if (status != DATAMOVER_OK) { + return status; + } + status = datamover_wait_done(timeout); + return status; +} + +datamover_status_t datamover_fold(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_c, uint32_t size_h, uint32_t size_w) { + // Converts unfolded tensor (P,N,C) to folded ("normal") tensor (C,H,W), patch size P = 2x2 = 4 + // matrix_in: input tensor in unfolded (P,N=(H*W)/P,C) layout + // matrix_out: output tensor in folded (C,H,W) layout + // size_c, size_h, size_w: dimensions of the folded (OUTPUT) tensor + const int P = 4; // Patch size (number of elements in a patch), only tested for P=4 (2x2) + const int side_P = 2; // Patch sidelength + int acq_to = 1000000; + int job_id = -1; + uint32_t c_tiles = (size_c + DATAMOVER_BANDWIDTH_ELEMS - 1) / DATAMOVER_BANDWIDTH_ELEMS; // number of tiles in C dimension (rounded up) + uint32_t w_tiles = (size_w + DATAMOVER_BANDWIDTH_ELEMS - 1) / DATAMOVER_BANDWIDTH_ELEMS; // number of tiles in W dimension (rounded up) + + while ((job_id = datamover_acquire_task()) < 0 && --acq_to) {} + if (acq_to == 0) { + return DATAMOVER_TO; + } + datamover_in_set((uint32_t)matrix_in); + datamover_out_set((uint32_t)matrix_out); + datamover_tot_len_set(c_tiles * w_tiles * DATAMOVER_BANDWIDTH_ELEMS * size_h); + datamover_in_d0_set(size_c * size_h * size_w / P, side_P); + datamover_in_d1_set(size_c, (w_tiles * DATAMOVER_BANDWIDTH_ELEMS) / side_P); + datamover_in_d2_set(DATAMOVER_BANDWIDTH_ELEMS, c_tiles); + datamover_in_d3_set(size_c * size_h * size_w / side_P, side_P); + datamover_out_d0_set(size_h * size_w, c_tiles * DATAMOVER_BANDWIDTH_ELEMS); + datamover_out_d1_set(DATAMOVER_BANDWIDTH_ELEMS, w_tiles); + datamover_out_d2_set(size_w, size_h); + datamover_out_d3_set(0, 0); + datamover_in_out_d4_stride_set(0, size_c * size_w / side_P); + datamover_matrix_dim_set(size_w, size_h); + datamover_channels_set(size_c * size_h * size_w, size_c); + datamover_ctrl_engine_set(DATAMOVER_FOLD, 0x3, 0xF, DATAMOVER_TRANSP_1ELEM); + + datamover_trigger_task(); + return DATAMOVER_OK; +} + +datamover_status_t datamover_fold_blocking(uint8_t *matrix_in, uint8_t *matrix_out, uint32_t size_c, uint32_t size_h, uint32_t size_w, uint64_t timeout) { + datamover_status_t status; + status = datamover_fold(matrix_in, matrix_out, size_c, size_h, size_w); + if (status != DATAMOVER_OK) { + return status; + } + status = datamover_wait_done(timeout); + return status; +} diff --git a/test/hal_datamover.h b/test/hal_datamover.h index bf960e7..08df498 100644 --- a/test/hal_datamover.h +++ b/test/hal_datamover.h @@ -1,155 +1,154 @@ -/* - * Copyright (C) 2020 ETH Zurich and University of Bologna - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Authors: Francesco Conti - */ - -#include +// Copyright 2025 ETH Zurich and University of Bologna. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Authors: Sergio Mazzola +// Cyrill Durrer +// Daniel Keller +// Francesco Conti #ifndef __HAL_DATAMOVER_H__ #define __HAL_DATAMOVER_H__ -/* REGISTER MAP */ - -// global address map + event IDs -#define DATAMOVER_ADDR_BASE 0x00201000 -#define CLUS_CTRL_ADDR_BASE 0x00200000 -#define DATAMOVER_EVT0 12 -#define DATAMOVER_EVT1 13 - -// commands -#define DATAMOVER_COMMIT_AND_TRIGGER 0x00 -#define DATAMOVER_ACQUIRE 0x04 -#define DATAMOVER_FINISHED 0x08 -#define DATAMOVER_STATUS 0x0c -#define DATAMOVER_RUNNING_JOB 0x10 -#define DATAMOVER_SOFT_CLEAR 0x14 -#define DATAMOVER_SWSYNC 0x18 -#define DATAMOVER_URISCY_IMEM 0x1c - -// job configuration ToDo(cdurrer): OUTDATED! Use HAL from Konark-cluster repo! -#define DATAMOVER_REGISTER_OFFS 0x40 -#define DATAMOVER_REGISTER_CXT0_OFFS 0x80 -#define DATAMOVER_REGISTER_CXT1_OFFS 0x120 -#define DATAMOVER_REG_IN_PTR 0x00 -#define DATAMOVER_REG_OUT_PTR 0x04 -#define DATAMOVER_REG_TOT_LEN 0x08 -#define DATAMOVER_REG_IN_D0_LEN 0x0c -#define DATAMOVER_REG_IN_D0_STRIDE 0x10 -#define DATAMOVER_REG_IN_D1_LEN 0x14 -#define DATAMOVER_REG_IN_D1_STRIDE 0x18 -#define DATAMOVER_REG_IN_D2_STRIDE 0x1c -#define DATAMOVER_REG_OUT_D0_LEN 0x20 -#define DATAMOVER_REG_OUT_D0_STRIDE 0x24 -#define DATAMOVER_REG_OUT_D1_LEN 0x28 -#define DATAMOVER_REG_OUT_D1_STRIDE 0x2c -#define DATAMOVER_REG_OUT_D2_STRIDE 0x30 - -// cluster controller register offset and bits -#define CLUS_CTRL_DATAMOVER_OFFS 0x18 -#define CLUS_CTRL_DATAMOVER_CG_EN_MASK 0x800 -#define CLUS_CTRL_DATAMOVER_HCI_PRIO_MASK 0x100 -#define CLUS_CTRL_DATAMOVER_HCI_MAXSTALL_MASK 0xff - -// others -#define DATAMOVER_COMMIT_CMD 1 -#define DATAMOVER_TRIGGER_CMD 0 -#define DATAMOVER_SOFT_CLEAR_ALL 0 -#define DATAMOVER_SOFT_CLEAR_STATE 1 - -/* LOW-LEVEL HAL */ -// For all the following functions we use __builtin_pulp_OffsetedWrite and __builtin_pulp_OffsetedRead -// instead of classic load/store because otherwise the compiler is not able to correctly factorize -// the DATAMOVER base in case several accesses are done, ending up with twice more code -#if defined(__riscv__) && !defined(RV_ISA_RV32) - #define DATAMOVER_WRITE_CMD(offset, value) __builtin_pulp_OffsetedWrite(value, (int volatile *)(DATAMOVER_ADDR_BASE), offset) - #define DATAMOVER_WRITE_CMD_BE(offset, value, be) *(char volatile *)(DATAMOVER_ADDR_BASE + offset + be) = value - // #define DATAMOVER_READ_CMD(offset) (__builtin_pulp_OffsetedRead(*(int volatile *)(DATAMOVER_ADDR_BASE), offset)) - #define DATAMOVER_READ_CMD(ret, offset) ret = (*(int volatile *)(DATAMOVER_ADDR_BASE + offset)) - - #define DATAMOVER_WRITE_REG(offset, value) __builtin_pulp_OffsetedWrite(value, (int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_OFFS), offset) - #define DATAMOVER_WRITE_REG_BE(offset, value, be) *(char volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_OFFS + offset + be) = value - // #define DATAMOVER_READ_REG(offset) (__builtin_pulp_OffsetedRead(*(int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_OFFS), offset)) - #define DATAMOVER_READ_REG(ret, offset) ret = (*(int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_OFFS + offset)) - - #define DATAMOVER_WRITE_REG_CXT0(offset, value) __builtin_pulp_OffsetedWrite(value, (int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT0_OFFS), offset) - #define DATAMOVER_WRITE_REG_CXT0_BE(offset, value, be) *(char volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT0_OFFS + offset + be) = value - #define DATAMOVER_READ_REG_CXT0(offset) (__builtin_pulp_OffsetedRead(*(int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT0_OFFS), offset)) - - #define DATAMOVER_WRITE_REG_CXT1(offset, value) __builtin_pulp_OffsetedWrite(value, (int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT1_OFFS), offset) - #define DATAMOVER_WRITE_REG_CXT1_BE(offset, value, be) *(char volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT1_OFFS + offset + be) = value - #define DATAMOVER_READ_REG_CXT1(offset) (__builtin_pulp_OffsetedRead(*(int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT1_OFFS), offset)) -#else - #define DATAMOVER_WRITE_CMD(offset, value) *(int volatile *)(DATAMOVER_ADDR_BASE + offset) = value - #define DATAMOVER_WRITE_CMD_BE(offset, value, be) *(char volatile *)(DATAMOVER_ADDR_BASE + offset + be) = value - #define DATAMOVER_READ_CMD(ret, offset) ret = (*(int volatile *)(DATAMOVER_ADDR_BASE + offset)) +#include + +#include "konark_cluster_raw_addrmap.h" + +/////////// +// Archi // +/////////// + +#define DATAMOVER_BASE_ADDR KONARK_CLUSTER_ADDRMAP_CLUSTER_HWPE_SUBSYSTEM_HWPE_DATAMOVER_BASE_ADDR - #define DATAMOVER_WRITE_REG(offset, value) *(int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_OFFS + offset) = value - #define DATAMOVER_WRITE_REG_BE(offset, value, be) *(char volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_OFFS + offset + be) = value - #define DATAMOVER_READ_REG(ret, offset) ret = (*(int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_OFFS + offset)) +/* Architecture */ - #define DATAMOVER_WRITE_REG_CXT0(offset, value) __builtin_pulp_OffsetedWrite(value, (int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT0_OFFS), offset) - #define DATAMOVER_WRITE_REG_CXT0_BE(offset, value, be) *(char volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT0_OFFS + offset + be) = value - #define DATAMOVER_READ_REG_CXT0(offset) (__builtin_pulp_OffsetedRead(*(int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT0_OFFS), offset)) +#define DATAMOVER_BANDWIDTH 512 +#define DATAMOVER_WORD_WIDTH 64 +#define DATAMOVER_ELEM_WIDTH 8 +#define DATAMOVER_MISALIGNED_ACCESSES 0 - #define DATAMOVER_WRITE_REG_CXT1(offset, value) __builtin_pulp_OffsetedWrite(value, (int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT1_OFFS), offset) - #define DATAMOVER_WRITE_REG_CXT1_BE(offset, value, be) *(char volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT1_OFFS + offset + be) = value - #define DATAMOVER_READ_REG_CXT1(offset) (__builtin_pulp_OffsetedRead(*(int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_REGISTER_CXT1_OFFS), offset)) +#if DATAMOVER_MISALIGNED_ACCESSES + #define DATAMOVER_BANDWIDTH_ALIGNED (DATAMOVER_BANDWIDTH - DATAMOVER_WORD_WIDTH) +#else + #define DATAMOVER_BANDWIDTH_ALIGNED (DATAMOVER_BANDWIDTH) +#endif +#define DATAMOVER_BANDWIDTH_ELEMS (DATAMOVER_BANDWIDTH_ALIGNED / DATAMOVER_ELEM_WIDTH) +#define DATAMOVER_WORD_ELEMS (DATAMOVER_WORD_WIDTH / DATAMOVER_ELEM_WIDTH) + +/* Registers */ +// To access a register add: DATAMOVER_BASE_ADDR + context offset + register offset + +#define DATAMOVER_REGISTER_OFFSET 0x40 // Alias of DATAMOVER_REGISTER_CXT0_OFFS +#define DATAMOVER_REGISTER_CXT0_OFFS 0x80 // Regfile context 0 +#define DATAMOVER_REGISTER_CXT1_OFFS 0x120 // Regfile context 1 + +#define DATAMOVER_REG_IN_PTR_OFFSET 0x00 // Input pointer +#define DATAMOVER_REG_OUT_PTR_OFFSET 0x04 // Output pointer +#define DATAMOVER_REG_TOT_LEN_OFFSET 0x08 // Total length in number of accesses (BW) +#define DATAMOVER_REG_IN_D0_OFFSET 0x0C // [31:16] in_d0_stride; [15:0] in_d0_len +#define DATAMOVER_REG_IN_D1_OFFSET 0x10 // [31:16] in_d1_stride; [15:0] in_d1_len +#define DATAMOVER_REG_IN_D2_OFFSET 0x14 // [31:16] in_d2_stride; [15:0] in_d2_len +#define DATAMOVER_REG_IN_D3_OFFSET 0x18 // [31:16] in_d3_stride; [15:0] in_d3_len +#define DATAMOVER_REG_OUT_D0_OFFSET 0x1C // [31:16] out_d0_stride; [15:0] out_d0_len +#define DATAMOVER_REG_OUT_D1_OFFSET 0x20 // [31:16] out_d1_stride; [15:0] out_d1_len +#define DATAMOVER_REG_OUT_D2_OFFSET 0x24 // [31:16] out_d2_stride; [15:0] out_d2_len +#define DATAMOVER_REG_OUT_D3_OFFSET 0x28 // [31:16] out_d3_stride; [15:0] out_d3_len +#define DATAMOVER_REG_IN_OUT_D4_STRIDE_OFFSET 0x2C // [31:16] out_d4_stride; [15:0] in_d4_stride (d4_len unnecessary due to tot_len) +#define DATAMOVER_REG_MATRIX_DIM_OFFSET 0x30 // [31:16] tensor_size_n; [15:0] tensor_size_m +#define DATAMOVER_REG_CHANNELS_OFFSET 0x34 // [31:11] total_elements = num_channels * size_m * size_n (pre-compute to save HW resources); [10:0] num_channels (for unfolding/folding) +#define DATAMOVER_REG_CTRL_ENGINE_OFFSET 0x38 // [15:12] write_dim_en; [11:8] read_dim_en; [7:3] datamover_mode; [2:0] transp_mode (LSB: 000=none, 001=1 elem, 010=2 elem, 100=4 elem) + + +/////////// +// Types // +/////////// +typedef enum { + DATAMOVER_COPY = 0x0, + DATAMOVER_TRANSP = 0x1, + DATAMOVER_CIM_LAYOUT = 0x2, + DATAMOVER_CIM_LAYOUT_TRANSPOSE = 0x3, + DATAMOVER_UNFOLD = 0x4, + DATAMOVER_FOLD = 0x5 +} datamover_mode_t; // Must match datamover_mode_e in datamover_package.sv +typedef enum { + DATAMOVER_TRANSP_NONE = 0x0, + DATAMOVER_TRANSP_1ELEM = 0x1, + DATAMOVER_TRANSP_2ELEM = 0x2, + DATAMOVER_TRANSP_4ELEM = 0x4 +} datamover_transp_mode_t; // Must match transp_mode_e in datamover_package.sv + +typedef enum { + DATAMOVER_OK = 0, // Success + DATAMOVER_TO, // Timeout + DATAMOVER_ERR // Generic error +} datamover_status_t; + +///////////// +// Defines // +///////////// + +#if VERBOSE +/* Verbose read/write register */ +#define __HAL_DATAMOVER_REG_WRITE(base, offset, value) do { \ + *(volatile uint32_t *)(base + offset) = value; \ + printf("__HAL_DATAMOVER_REG_WRITE: Addr 0x%08x <= 0x%08x\n", (uint32_t)(base + offset), (uint32_t)(value)); \ + } while(0) +#define __HAL_DATAMOVER_REG_READ(base, offset) ({ \ + uint32_t read_value = *(volatile uint32_t *)(base + offset); \ + printf("__HAL_DATAMOVER_REG_READ: Addr 0x%08x => 0x%08x\n", (uint32_t)(base + offset), (uint32_t)(read_value)); \ + read_value; \ + }) +#else +/* Normal read/write register */ +#define __HAL_DATAMOVER_REG_WRITE(base, offset, value) *(volatile uint32_t *)(base + offset) = value +#define __HAL_DATAMOVER_REG_READ(base, offset) *(volatile uint32_t *)(base + offset) #endif -#define DATAMOVER_CG_ENABLE() *(volatile int*) (CLUS_CTRL_ADDR_BASE + CLUS_CTRL_DATAMOVER_OFFS) |= CLUS_CTRL_DATAMOVER_CG_EN_MASK -#define DATAMOVER_CG_DISABLE() *(volatile int*) (CLUS_CTRL_ADDR_BASE + CLUS_CTRL_DATAMOVER_OFFS) &= ~CLUS_CTRL_DATAMOVER_CG_EN_MASK - -#define DATAMOVER_SETPRIORITY_CORE() *(volatile int*) (CLUS_CTRL_ADDR_BASE + CLUS_CTRL_DATAMOVER_OFFS) &= ~CLUS_CTRL_DATAMOVER_HCI_PRIO_MASK -#define DATAMOVER_SETPRIORITY_DATAMOVER() *(volatile int*) (CLUS_CTRL_ADDR_BASE + CLUS_CTRL_DATAMOVER_OFFS) |= CLUS_CTRL_DATAMOVER_HCI_PRIO_MASK - -#define DATAMOVER_RESET_MAXSTALL() *(volatile int*) (CLUS_CTRL_ADDR_BASE + CLUS_CTRL_DATAMOVER_OFFS) &= ~CLUS_CTRL_DATAMOVER_HCI_MAXSTALL_MASK -#define DATAMOVER_SET_MAXSTALL(val) *(volatile int*) (CLUS_CTRL_ADDR_BASE + CLUS_CTRL_DATAMOVER_OFFS) |= (val & CLUS_CTRL_DATAMOVER_HCI_MAXSTALL_MASK) - -#define DATAMOVER_BARRIER_NOSTATUS() eu_evt_maskWaitAndClr (1 << DATAMOVER_EVT0) -#define DATAMOVER_BARRIER() do { eu_evt_maskWaitAndClr (1 << DATAMOVER_EVT0); } while((*(int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_STATUS)) != 0) -#define DATAMOVER_BUSYWAIT() do { } while((*(int volatile *)(DATAMOVER_ADDR_BASE + DATAMOVER_STATUS)) != 0) -#define DATAMOVER_BARRIER_ACQUIRE(job_id) job_id = DATAMOVER_READ_CMD(job_id, DATAMOVER_ACQUIRE); \ - while(job_id < 0) { eu_evt_maskWaitAndClr (1 << DATAMOVER_EVT0); DATAMOVER_READ_CMD(job_id, DATAMOVER_ACQUIRE); }; - -/* UTILITY FUNCTIONS */ -int DATAMOVER_compare_int(uint32_t *actual_y, uint32_t *golden_y, int len) { - uint32_t actual_word = 0; - uint32_t golden_word = 0; - uint32_t actual = 0; - uint32_t golden = 0; - - int errors = 0; - int non_zero_values = 0; - - for (int i=0; i +// Cyrill Durrer +// Sergio Mazzola + +#include + +#include "konark/hal_hwpe.h" + +///////////// +// Drivers // +///////////// + +// Access to HWPE mandatory registers + +void hwpe_task_queue_release_and_run(uint32_t hwpe_base_addr) { + __HAL_HWPE_REG_WRITE(hwpe_base_addr, HWPE_TRIGGER_OFFSET, 0); +} + +void hwpe_task_queue_release(uint32_t hwpe_base_addr) { + __HAL_HWPE_REG_WRITE(hwpe_base_addr, HWPE_TRIGGER_OFFSET, 1); +} + +int hwpe_task_queue_acquire_task(uint32_t hwpe_base_addr) { + int read_value = (int)__HAL_HWPE_REG_READ(hwpe_base_addr, HWPE_ACQUIRE_OFFSET); + return (int)read_value; +} + +uint32_t hwpe_finished(uint32_t hwpe_base_addr) { + return (uint32_t)__HAL_HWPE_REG_READ(hwpe_base_addr, HWPE_FINISHED_OFFSET); +} + +uint32_t hwpe_task_queue_status(uint32_t hwpe_base_addr) { + return (uint32_t)__HAL_HWPE_REG_READ(hwpe_base_addr, HWPE_STATUS_OFFSET); +} + +uint8_t hwpe_last_task_id(uint32_t hwpe_base_addr) { + return (uint8_t)__HAL_HWPE_REG_READ(hwpe_base_addr, HWPE_RUNNING_JOB_OFFSET); +} + +void hwpe_soft_clear(uint32_t hwpe_base_addr) { + __HAL_HWPE_REG_WRITE(hwpe_base_addr, HWPE_SOFT_CLEAR_OFFSET, 0); + // for (volatile int i = 0; i < 10; i++); // ToDo: Is this necessary to ensure the soft clear takes effect before next commands? +} diff --git a/test/hal_hwpe.h b/test/hal_hwpe.h new file mode 100644 index 0000000..b835608 --- /dev/null +++ b/test/hal_hwpe.h @@ -0,0 +1,65 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Authors: Daniel Keller +// Cyrill Durrer +// Sergio Mazzola + +#ifndef __HAL_HWPE_H__ +#define __HAL_HWPE_H__ + +#include + +/////////// +// Archi // +/////////// + +// Register offsets of HWPE Ctrl (32-bit registers) +#define HWPE_TRIGGER_OFFSET 0*4 +#define HWPE_ACQUIRE_OFFSET 1*4 +#define HWPE_FINISHED_OFFSET 2*4 +#define HWPE_STATUS_OFFSET 3*4 +#define HWPE_RUNNING_JOB_OFFSET 4*4 +#define HWPE_SOFT_CLEAR_OFFSET 5*4 +// #define HWPE_RESERVED_OFFSET 6*4 +#define HWPE_SWSYNC_OFFSET 7*4 + +///////////// +// Defines // +///////////// + +#define __HAL_HWPE_VERBOSE 0 + +#if __HAL_HWPE_VERBOSE +/* Verbose read/write register */ +#include "printf.h" +#define __HAL_HWPE_REG_WRITE(base, offset, value) do { \ + *(volatile uint32_t *)(base + offset) = value; \ + printf("__HAL_HWPE_REG_WRITE: Addr 0x%08x <= 0x%08x\n", (uint32_t)(base + offset), (uint32_t)(value)); \ + } while(0) +#define __HAL_HWPE_REG_READ(base, offset) ({ \ + uint32_t read_value = *(volatile uint32_t *)(base + offset); \ + printf("__HAL_HWPE_REG_READ: Addr 0x%08x => 0x%08x\n", (uint32_t)(base + offset), (uint32_t)(read_value)); \ + read_value; \ + }) +#else +/* Normal read/write register */ +#define __HAL_HWPE_REG_WRITE(base, offset, value) *(volatile uint32_t *)(base + offset) = value +#define __HAL_HWPE_REG_READ(base, offset) *(volatile uint32_t *)(base + offset) +#endif + +//////////////// +// Prototypes // +//////////////// + +// Drivers +void hwpe_task_queue_release_and_run(uint32_t hwpe_base_addr); +void hwpe_task_queue_release(uint32_t hwpe_base_addr); +int hwpe_task_queue_acquire_task(uint32_t hwpe_base_addr); +uint32_t hwpe_finished(uint32_t hwpe_base_addr); +uint32_t hwpe_task_queue_status(uint32_t hwpe_base_addr); +uint8_t hwpe_last_task_id(uint32_t hwpe_base_addr); +void hwpe_soft_clear(uint32_t hwpe_base_addr); + +#endif // __HAL_HWPE_H__ diff --git a/test/lfsr32.c b/test/lfsr32.c deleted file mode 100644 index 6d0635b..0000000 --- a/test/lfsr32.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2019-2020 Francesco Conti - * - * Adapted from https://github.com/russm/lfsr64 - * This is a simple 32-bit linear feedback shift register, printing - * pseudo-random bytes to stdout. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "pmsis.h" -#include -#include -#include "lfsr32.h" - -#define DEFAULT_SEED 0xdeadbeef -#define USE_BYTE_FEEDBACK - -int glob_errors; - -#ifdef USE_BYTE_FEEDBACK -// not actually extern, just down the bottom -extern uint32_t __lfsr_byte_feedback[]; -#else -uint32_t *__lfsr_byte_feedback; -#endif - -#ifndef USE_BYTE_FEEDBACK -uint32_t __lfsr_iter_bit(uint32_t lfsr) { - return (lfsr & 1) ? ((lfsr >> 1) ^ FEEDBACK) : (lfsr >> 1); -} -#endif - -uint32_t __lfsr_iter_byte(uint32_t lfsr, uint32_t *lfsr_byte_feedback) { -#ifdef USE_BYTE_FEEDBACK - // this shift/lookup/xor is equivalent to 8 iterations of - // lfsr = (lfsr & 1) ? ((lfsr >> 1) ^ 0x800000000000000D) : (lfsr >> 1); - return (lfsr >> 8) ^ lfsr_byte_feedback[lfsr & 0xff]; -#else - uint32_t l = lfsr; - for(int i=0; i<8; i++) - l = __lfsr_iter_bit(l); - return l; -#endif -} - -uint32_t __lfsr_iter_word(uint32_t lfsr, uint32_t *lfsr_byte_feedback) { - uint32_t l = __lfsr_iter_byte(lfsr, lfsr_byte_feedback); - l = __lfsr_iter_byte(l, lfsr_byte_feedback); - l = __lfsr_iter_byte(l, lfsr_byte_feedback); - return __lfsr_iter_byte(l, lfsr_byte_feedback); -} - -int generate_random_buffer(int addr_first, int addr_last, uint32_t seed) { - uint32_t lfsr = seed; - for(uint32_t addr=addr_first; addr - * - * Adapted from https://github.com/russm/lfsr64 - * This is a simple 32-bit linear feedback shift register, printing - * pseudo-random bytes to stdout. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEFAULT_SEED 0xdeadbeef -#define USE_BYTE_FEEDBACK - -int generate_random_buffer(int addr_first, int addr_last, uint32_t seed); -int check_random_buffer(int addr_first, int addr_last, uint32_t seed); diff --git a/test/test_datamover.c b/test/test_datamover.c deleted file mode 100644 index dfe0f7d..0000000 --- a/test/test_datamover.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2019-2020 ETH Zurich and University of Bologna - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Authors: Francesco Conti - */ - -#include "pmsis.h" -#include "stdio.h" -#include -#include "hal_datamover.h" -#include "lfsr32.h" - -#define DATA_SIZE (16*1024) -#define DATAMOVER_BW (256 / 8) - -static int ret_value; - -static void pe_entry(void *arg) { - - printf("Entered cluster on cluster %d core %d\n", pi_cluster_id(), pi_core_id()); - - pi_cl_team_barrier(); - - int errors = 0; - - if (pi_core_id() == 0) { - - uint8_t volatile *x = (uint8_t volatile *) pi_cl_l1_malloc(NULL, DATA_SIZE); - uint8_t volatile *y = (uint8_t volatile *) pi_cl_l1_malloc(NULL, DATA_SIZE); - generate_random_buffer((int) x, (int) x + DATA_SIZE, DEFAULT_SEED); - - // enable clock - DATAMOVER_CG_ENABLE(); - - // setup HCI - DATAMOVER_SETPRIORITY_DATAMOVER(); // priority to DATAMOVER w.r.t. cores, DMA - DATAMOVER_RESET_MAXSTALL(); // reset maximum stall - DATAMOVER_SET_MAXSTALL(8); // set maximum consecutive stall to 8 cycles for cores, DMA side - - // soft-clear DATAMOVER - DATAMOVER_WRITE_CMD(DATAMOVER_SOFT_CLEAR, DATAMOVER_SOFT_CLEAR_ALL); - for(volatile int kk=0; kk<10; kk++); - - // acquire job - int job_id = -1; - DATAMOVER_BARRIER_ACQUIRE(job_id); - - // set up datamover - DATAMOVER_WRITE_REG(DATAMOVER_REG_IN_PTR, x); - DATAMOVER_WRITE_REG(DATAMOVER_REG_OUT_PTR, y); - DATAMOVER_WRITE_REG(DATAMOVER_REG_TOT_LEN, DATA_SIZE / DATAMOVER_BW); - DATAMOVER_WRITE_REG(DATAMOVER_REG_IN_D0_LEN, DATA_SIZE / DATAMOVER_BW); - DATAMOVER_WRITE_REG(DATAMOVER_REG_IN_D0_STRIDE, DATAMOVER_BW); - DATAMOVER_WRITE_REG(DATAMOVER_REG_IN_D1_LEN, DATA_SIZE / DATAMOVER_BW); - DATAMOVER_WRITE_REG(DATAMOVER_REG_IN_D1_STRIDE, 0); - DATAMOVER_WRITE_REG(DATAMOVER_REG_IN_D2_STRIDE, 0); - DATAMOVER_WRITE_REG(DATAMOVER_REG_OUT_D0_LEN, DATA_SIZE / DATAMOVER_BW); - DATAMOVER_WRITE_REG(DATAMOVER_REG_OUT_D0_STRIDE, DATAMOVER_BW); - DATAMOVER_WRITE_REG(DATAMOVER_REG_OUT_D1_LEN, DATA_SIZE / DATAMOVER_BW); - DATAMOVER_WRITE_REG(DATAMOVER_REG_OUT_D1_STRIDE, 0); - DATAMOVER_WRITE_REG(DATAMOVER_REG_OUT_D2_STRIDE, 0); - - // commit and trigger datamover operation - DATAMOVER_WRITE_CMD(DATAMOVER_COMMIT_AND_TRIGGER, DATAMOVER_TRIGGER_CMD); - - // wait for end of computation - DATAMOVER_BARRIER(); - - // disable clock - DATAMOVER_CG_DISABLE(); - - // set priority to core side - DATAMOVER_SETPRIORITY_CORE(); - - ret_value = check_random_buffer((int) y, (int) y + DATA_SIZE, DEFAULT_SEED); - - } - pi_cl_team_barrier(); -} - -static void cluster_entry(void *arg) { - pi_cl_team_fork(0, pe_entry, 0); -} - -void test_kickoff(void *arg) -{ - struct pi_device cluster_dev; - struct pi_cluster_conf conf; - struct pi_cluster_task task; - ret_value = 0; - - pi_cluster_conf_init(&conf); - conf.id = 0; - - pi_open_from_conf(&cluster_dev, &conf); - - pi_cluster_open(&cluster_dev); - - pi_cluster_task(&task, cluster_entry, NULL); - - pi_cluster_send_task_to_cl(&cluster_dev, &task); - - pi_cluster_close(&cluster_dev); - - pmsis_exit(ret_value); -} - -int main() -{ - return pmsis_kickoff((void *)test_kickoff); -} diff --git a/verif/python/datamover_golden_model.py b/verif/python/datamover_golden_model.py new file mode 100644 index 0000000..1595783 --- /dev/null +++ b/verif/python/datamover_golden_model.py @@ -0,0 +1,224 @@ +# Copyright 2026 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Authors: Cyrill Durrer + +import os +import math +import argparse +import numpy as np + +def data_header_format(data, elements_per_line=16): + lines = [] + for i in range(0, len(data), elements_per_line): + line_elements = data[i:i + elements_per_line] + formatted_elements = [f"0x{elem:02x}" for elem in line_elements] + if i + elements_per_line < len(data): + line = " " + ", ".join(formatted_elements) + "," + else: + line = " " + ", ".join(formatted_elements) + lines.append(line) + return lines + +def write_data_header_file(output_dir, input_tensor, output_tensor, config_params, filename="data.h"): + os.makedirs(output_dir, exist_ok=True) + filepath = os.path.join(output_dir, filename) + + input_flat = np.asarray(input_tensor, dtype=np.uint8).reshape(-1).tolist() + output_flat = np.asarray(output_tensor, dtype=np.uint8).reshape(-1).tolist() + + data_h_string = [ + "#pragma once", + "", + "#include ", + "", + "// Configuration Parameters", + f"#define DATAMOVER_MODE {config_params['datamover_mode']}", + f"#define TRANSP_MODE {config_params['transp_mode']}", + f"#define CIM_MODE {config_params['cim_mode']}", + f"#define ROW_TILE_SIZE {config_params['row_tile_size']}", + f"#define SIZE_C {config_params['size_c']}", + f"#define SIZE_M {config_params['size_m']}", + f"#define SIZE_N {config_params['size_n']}", + "", + "uint8_t golden_in [SIZE_C*SIZE_M*SIZE_N] = {", + # "PI_L1 uint8_t golden_in [SIZE_C*SIZE_M*SIZE_N] = {", # PI_L1 only for GVSoC (siracusa) + ] + data_h_string.extend(data_header_format(input_flat)) + data_h_string.extend([ + "};", + "", + "uint8_t golden_out [SIZE_C*SIZE_M*SIZE_N] = {", + # "PI_L1 uint8_t golden_out [SIZE_C*SIZE_M*SIZE_N] = {", # PI_L1 only for GVSoC (siracusa) + ]) + data_h_string.extend(data_header_format(output_flat)) + data_h_string.extend([ + "};", + "", + ]) + + with open(filepath, "w", encoding="utf-8") as file: + file.write("\n".join(data_h_string)) + + return filepath + +def cim_layout(tensor, row_tile_size, size_m, size_n): + # Input tensor shape: (size_m, size_n) + # Output shape: (1, size_m * size_n) + # Transformation: columns are grouped into tiles of row_tile_size; + # for each tile, all rows' slices are stored consecutively in the output. + + # size_m, size_n = tensor.shape + n_tiles = size_n // row_tile_size + leftover = size_n % row_tile_size + + # Reshape to (size_m, n_tiles, row_tile_size), transpose to (n_tiles, size_m, row_tile_size), flatten + parts = [tensor[:, :n_tiles * row_tile_size].reshape(size_m, n_tiles, row_tile_size).transpose(1, 0, 2).reshape(-1)] + if leftover > 0: + parts.append(tensor[:, n_tiles * row_tile_size:].reshape(-1)) + return np.concatenate(parts).reshape(1, -1) + +def cim_layout_reverse(tensor, row_tile_size, size_m, size_n): + # Input tensor shape: (1, size_m * size_n) in CIM layout + # Output shape: (size_m, size_n) in row-major layout + # size_m, size_n are the dimensions of the original row-major tensor + + n_tiles = size_n // row_tile_size + leftover = size_n % row_tile_size + flat = tensor.reshape(-1) + + # Reshape to (n_tiles, size_m, row_tile_size), transpose to (size_m, n_tiles, row_tile_size) + complete = flat[:n_tiles * size_m * row_tile_size].reshape(n_tiles, size_m, row_tile_size).transpose(1, 0, 2) + if leftover > 0: + leftover_part = flat[n_tiles * size_m * row_tile_size:].reshape(size_m, leftover) + return np.concatenate([complete.reshape(size_m, n_tiles * row_tile_size), leftover_part], axis=1) + return complete.reshape(size_m, size_n) + +def cim_layout_transpose(tensor, row_tile_size, size_m, size_n): + # Input tensor in CIM-layout (size_m and size_n are the dimensions of the original row-major tensor) + # Output tensor: transposed input tensor in CIM-layout + row_major_tensor = cim_layout_reverse(tensor, row_tile_size, size_m, size_n) + row_major_transposed = np.transpose(row_major_tensor) + output_tensor = cim_layout(row_major_transposed, row_tile_size, size_n, size_m) + return output_tensor + + +def unfold(tensor, patch_size): + # Input tensor shape: (CHANNELS, HEIGHT, WIDTH) + # Output tensor shape: (PATCH_SIZE, NUM_PATCHES, CHANNELS) + channels, height, width = tensor.shape + patch_sidelength = int(math.sqrt(patch_size)) + assert (height % patch_sidelength == 0) and (width % patch_sidelength == 0), "Height and Width must be divisible by patch sidelength" + num_patches_h = height // patch_sidelength + num_patches_w = width // patch_sidelength + num_patches = num_patches_h * num_patches_w + tensor_unfolded = np.zeros((patch_size, num_patches, channels), dtype=tensor.dtype) + for p in range(patch_size): + for h in range(num_patches_h): + for w in range(num_patches_w): + n = h * num_patches_w + w + h_idx = h * patch_sidelength + (p // patch_sidelength) + w_idx = w * patch_sidelength + (p % patch_sidelength) + tensor_unfolded[p, n, :] = tensor[:, h_idx, w_idx] + return tensor_unfolded + +def fold(tensor, patch_size, num_channels, height, width): # Parameters: output tensor dimensions (CHW) + # Input tensor shape: (PATCH_SIZE, NUM_PATCHES, CHANNELS) -- calculated from output dimensions + # Output tensor shape: (CHANNELS, HEIGHT, WIDTH) -- num_channels, height, width are the folded dimensions + patch_sidelength = int(math.sqrt(patch_size)) + # size_n = (height * width) // patch_size + assert (height % patch_sidelength == 0) and (width % patch_sidelength == 0), "Height and Width must be divisible by patch sidelength" + num_patches_h = height // patch_sidelength + num_patches_w = width // patch_sidelength + tensor_folded = np.zeros((num_channels, height, width), dtype=tensor.dtype) + for p in range(patch_size): + for h in range(num_patches_h): + for w in range(num_patches_w): + n = h * num_patches_w + w + h_idx = h * patch_sidelength + (p // patch_sidelength) + w_idx = w * patch_sidelength + (p % patch_sidelength) + tensor_folded[:, h_idx, w_idx] = tensor[p, n, :] + return tensor_folded + +def main(): + parser = argparse.ArgumentParser(description="Datamover golden model generator") + parser.add_argument("--datamover-mode", type=int, default=0, dest="datamover_mode", help="0: copy, 1: transpose, 2: CIM layout, 3: CIM layout transpose, 4: unfold, 5: fold") + parser.add_argument("--transp-mode", type=int, default=1, dest="transp_mode", help="Transpose element width: 1, 2, or 4") + parser.add_argument("--cim-mode", type=int, default=0, dest="cim_mode", help="0: row-major -> CIM-layout, 1: CIM-layout -> row-major") + parser.add_argument("--row-tile-size", type=int, default=64, dest="row_tile_size", help="CIM row tile size (inner dimension)") + parser.add_argument("--size-c", type=int, default=1, dest="size_c", help="Tensor dimension C") + parser.add_argument("--size-m", type=int, default=1, dest="size_m", help="Tensor dimension M (rows)") + parser.add_argument("--size-n", type=int, default=1, dest="size_n", help="Tensor dimension N (columns)") + parser.add_argument("--patch-size", type=int, default=4, dest="patch_size", help="MobileViT patch size (unfold/fold)") + parser.add_argument("--memory-size", type=int, default=1048576, dest="memory_size", help="TCDM size in Bytes") + parser.add_argument("--output-filename",type=str, default="data.h", dest="output", help="Output header filename") + parser.add_argument("--count", action="store_true", dest="count", help="Use counting stimuli instead of random (for debugging)") + args = parser.parse_args() + + # Check configuration: maximum tensor size limited by memory divided by three (input, output, golden) + # required_elements = 3 * args.size_c * args.size_m * args.size_n + # assert required_elements <= args.memory_size, ( + # f"MEMORY_SIZE ({args.memory_size}) is too small: requires at least " + # f"3*SIZE_C*SIZE_M*SIZE_N = {required_elements} elements" + # ) + + # Create a tensor of size (SIZE_C, SIZE_M, SIZE_N) with random or counting values + if not args.count: + input_tensor = np.random.randint(0, 256, (args.size_c, args.size_m, args.size_n), dtype=np.uint8) + else: + input_tensor = np.arange(args.size_c * args.size_m * args.size_n, dtype=np.uint8).reshape((args.size_c, args.size_m, args.size_n)) + + # Generate golden output tensor based on the selected datamover mode + if args.datamover_mode==0: + output_tensor = input_tensor.copy() + elif args.datamover_mode==1: + t = args.transp_mode + if t not in [1, 2, 4]: + raise ValueError(f"Unsupported TRANSP_MODE: {t}. Supported modes: 1, 2, 4.") + C, M, N = input_tensor.shape + assert N % t == 0, f"SIZE_N ({N}) must be divisible by transp_mode ({t})" + output_tensor = input_tensor.reshape(C, M, N // t, t).transpose(0, 2, 1, 3).reshape(C, N // t, M * t) + elif args.datamover_mode==2: + if args.cim_mode == 0: # row-major -> CIM-layout + output_tensor = cim_layout(input_tensor.reshape(args.size_m, args.size_n), args.row_tile_size, args.size_m, args.size_n) + elif args.cim_mode == 1: # CIM-layout -> row-major (reverse of mode 0) + output_tensor = cim_layout_reverse(input_tensor.reshape(args.size_m, args.size_n), args.row_tile_size, args.size_m, args.size_n) + else: + raise ValueError(f"Unsupported CIM_MODE: {args.cim_mode}") + elif args.datamover_mode==3: + input_tensor = input_tensor.reshape(args.size_m, args.size_n) + output_tensor = cim_layout_transpose(input_tensor, args.row_tile_size, args.size_m, args.size_n) + elif args.datamover_mode==4: + output_tensor = unfold(input_tensor, args.patch_size) + elif args.datamover_mode==5: + # For fold mode, generate the input as an unfolded tensor by first unfolding a counting tensor + # base_tensor = np.arange(SIZE_C * SIZE_M * SIZE_N, dtype=np.uint8).reshape((SIZE_C, SIZE_M, SIZE_N)) + unfolded_tensor = unfold(input_tensor, args.patch_size) + input_tensor = unfolded_tensor.copy() # Use the unfolded tensor as input for fold mode + output_tensor = fold(unfolded_tensor, args.patch_size, args.size_c, args.size_m, args.size_n) + else: + raise ValueError(f"Unsupported DATAMOVER_MODE: {args.datamover_mode}") + + # print("Input Tensor:") + # print(input_tensor) + # print("\nGolden Output Tensor:") + # print(output_tensor) + + print("\nSuccessfully generated golden model output tensor.") + + output_dir = os.path.join(os.path.dirname(__file__), "data") + config_params = { + "datamover_mode": args.datamover_mode, + "transp_mode": args.transp_mode, + "cim_mode": args.cim_mode, + "row_tile_size": args.row_tile_size, + "size_c": args.size_c, + "size_m": args.size_m, + "size_n": args.size_n, + } + header_file = write_data_header_file(output_dir, input_tensor, output_tensor, config_params, args.output) + print(f"\nWrote golden header to: {header_file}") + +if __name__ == "__main__": + main() diff --git a/verif/python/datamover_golden_model_numpy.py b/verif/python/datamover_golden_model_numpy.py index b9f5d61..e465a07 100644 --- a/verif/python/datamover_golden_model_numpy.py +++ b/verif/python/datamover_golden_model_numpy.py @@ -17,8 +17,7 @@ DATAMOVER_MODE = 5 # 0: copy, 1: transpose, 2: CIM data layout conversion, 3: CIM layout transpose, 4: unfold (MobileViT), 5: fold (MobileViT), other values: not accepted TRANSP_MODE = 1 CIM_MODE = 0 -CIM_INNER_DIM = 64 -CIM_OUTER_DIM = 64 +ROW_TILE_SIZE = 64 SIZE_C = CHANNELS SIZE_M = HEIGHT SIZE_N = WIDTH @@ -56,11 +55,10 @@ def write_data_header_file(output_dir, input_matrix, output_matrix, config_param f"#define DATAMOVER_MODE {config_params['datamover_mode']}", f"#define TRANSP_MODE {config_params['transp_mode']}", f"#define CIM_MODE {config_params['cim_mode']}", - f"#define CIM_INNER_DIM {config_params['cim_inner_dim']}", - f"#define CIM_OUTER_DIM {config_params['cim_outer_dim']}", - f"#define SIZE_C {config_params['matrix_dim_c']}", - f"#define SIZE_M {config_params['matrix_dim_m']}", - f"#define SIZE_N {config_params['matrix_dim_n']}", + f"#define ROW_TILE_SIZE {config_params['row_tile_size']}", + f"#define SIZE_C {config_params['size_c']}", + f"#define SIZE_M {config_params['size_m']}", + f"#define SIZE_N {config_params['size_n']}", "", "PI_L1 uint8_t golden_in [SIZE_C*SIZE_M*SIZE_N] = {", # PI_L1 only for GVSoC (siracusa) ] @@ -167,11 +165,10 @@ def main(): "datamover_mode": DATAMOVER_MODE, "transp_mode": TRANSP_MODE, "cim_mode": CIM_MODE, - "cim_inner_dim": CIM_INNER_DIM, - "cim_outer_dim": CIM_OUTER_DIM, - "matrix_dim_c": SIZE_C, - "matrix_dim_m": SIZE_M, - "matrix_dim_n": SIZE_N, + "row_tile_size": ROW_TILE_SIZE, + "size_c": SIZE_C, + "size_m": SIZE_M, + "size_n": SIZE_N, } header_file = write_data_header_file(output_dir, input_tensor, output_tensor, config_params) print(f"\nWrote golden header to: {header_file}") diff --git a/verif/python/datamover_microarchitectural_model.py b/verif/python/datamover_microarchitectural_model.py index 2721fb2..f014461 100644 --- a/verif/python/datamover_microarchitectural_model.py +++ b/verif/python/datamover_microarchitectural_model.py @@ -6,6 +6,8 @@ import numpy as np from dataclasses import dataclass +# OUTDATED: replaced by datamover_golden_model.py + RANDOM_STIMULI = False # If False, stimuli are generated in a counting fashion # HW Parameters diff --git a/verif/python/generate_stimuli.py b/verif/python/generate_stimuli.py index 9b66ac5..4d6b724 100644 --- a/verif/python/generate_stimuli.py +++ b/verif/python/generate_stimuli.py @@ -3,7 +3,9 @@ import os import math -RANDOM_STIMULI = True # If False, stimuli are generated in a counting fashion +# OUTDATED + +RANDOM_STIMULI = False # If False, stimuli are generated in a counting fashion def extract_elements_from_word(word, word_width, elem_width): """Extract elements from a word based on the specified widths.""" @@ -147,11 +149,10 @@ def write_data_header_file(output_dir, input_matrix, output_matrix, config_param f"#define DATAMOVER_MODE {config_params['datamover_mode']}", f"#define TRANSP_MODE {config_params['transp_mode']}", f"#define CIM_MODE {config_params['cim_mode']}", - f"#define CIM_INNER_DIM {config_params['cim_inner_dim']}", - f"#define CIM_OUTER_DIM {config_params['cim_outer_dim']}", + f"#define ROW_TILE_SIZE {config_params['row_tile_size']}", f"#define SIZE_C {config_params['num_channels']}", - f"#define SIZE_M {config_params['matrix_dim_m']}", - f"#define SIZE_N {config_params['matrix_dim_n']}", + f"#define SIZE_M {config_params['size_m']}", + f"#define SIZE_N {config_params['size_n']}", "", "uint8_t golden_in [SIZE_C*SIZE_M*SIZE_N] = {", ] @@ -180,18 +181,7 @@ def transpose(matrix, size_m, size_n, transp_mode): return transposed -def cim_layout(matrix, size_m, size_n, cim_mode, cim_inner_dim, cim_outer_dim, word_width_elems): - if cim_mode == 0: # row-major -> A-Layout - row_tile_size = cim_inner_dim - elif cim_mode == 1: # row-major -> B-Layout - row_tile_size = cim_outer_dim - elif cim_mode == 2: # A-Layout -> row-major - row_tile_size = cim_inner_dim - elif cim_mode == 3: # B-Layout -> row-major - row_tile_size = cim_outer_dim - else: - raise ValueError("[GM] cim_mode must be 0 (A-Layout), 1 (B-Layout), 2 (A-Layout -> row-major), or 3 (B-Layout -> row-major).") - +def cim_layout(matrix, size_m, size_n, row_tile_size, word_width_elems): complete_n_tiles = size_n // row_tile_size leftover_columns = size_n % row_tile_size leftover_words = math.ceil(leftover_columns / word_width_elems) @@ -216,18 +206,7 @@ def cim_layout(matrix, size_m, size_n, cim_mode, cim_inner_dim, cim_outer_dim, w cim_matrix[0][index : index + leftover_columns] = chunk[i*leftover_columns:(i*leftover_columns)+leftover_columns] return cim_matrix -def cim_layout_reverse(matrix, size_m, size_n, cim_mode, cim_inner_dim, cim_outer_dim, word_width_elems): # dimensions of original row-major layout are used - if cim_mode == 0: # row-major -> A-Layout - row_tile_size = cim_inner_dim - elif cim_mode == 1: # A-Layout -> row-major - row_tile_size = cim_inner_dim - elif cim_mode == 2: # row-major -> B-Layout - row_tile_size = cim_outer_dim - elif cim_mode == 3: # B-Layout -> row-major - row_tile_size = cim_outer_dim - else: - raise ValueError("[GM] cim_mode must be 0 (row-major -> A-Layout), 1 (A-Layout -> row-major), 2 (row-major -> B-Layout), or 3 (B-Layout -> row-major).") - +def cim_layout_reverse(matrix, size_m, size_n, row_tile_size, word_width_elems): # dimensions of original row-major layout are used complete_n_tiles = size_n // row_tile_size leftover_columns = size_n % row_tile_size cim_matrix = [[0] * (size_m * size_n)] @@ -247,24 +226,6 @@ def cim_layout_reverse(matrix, size_m, size_n, cim_mode, cim_inner_dim, cim_oute print(f"Processing leftover columns for row {d0}: input index {input_index} to output index {output_index}") return cim_matrix -def unfold(tensor, patch_size): - # input tensor must be of shape CHW (channels, height (M), width (N)) - # output tensor produced in shape PNC (patch_size, num_patches, channels) - # patch_size: MobileViT: 2x2 = 4 (must be square) - channels = len(tensor) - height = len(tensor[0]) - width = len(tensor[0][0]) - patch_sidelength = int(math.sqrt(patch_size)) - if (height % patch_sidelength) != 0 or (width % patch_sidelength) != 0: - raise ValueError("[GM] Tensor height and width must be multiples of the patch sidelength.") - num_patches = (height * width) // patch_size # number of patches - tensor_unfolded = [[[0 for _ in range(channels)] for _ in range(num_patches)] for _ in range(patch_size)] - for p in range(patch_size): - for n in range(num_patches): - - tensor_unfolded[p][n] = [ tensor[c][h][w] for c in range(channels)] - - def main(): # Parse command-line arguments parser = argparse.ArgumentParser(description="Memory Read/Write Simulation with Word-Aligned Strides") @@ -278,11 +239,10 @@ def main(): parser.add_argument("--datamover_mode", type=int, default=0, help="Datamover mode (0=normal, 1=CIM)") parser.add_argument("--transp_mode", type=int, default=0, help="Transposition mode (3'b000 = none, 3'b001 = 1 elem, 3'b010 = 2 elem, 3'b100 = 4 elem)") parser.add_argument("--cim_mode", type=int, default=0, help="CIM mode (0=normal, 1=CIM)") - parser.add_argument("--cim_inner_dim", type=int, default=4, help="CIM inner dimension") - parser.add_argument("--cim_outer_dim", type=int, default=4, help="CIM outer dimension") + parser.add_argument("--row_tile_size", type=int, default=4, help="Row tile size") parser.add_argument("--num_channels", type=int, default=1, help="Number of channels") - parser.add_argument("--matrix_dim_m", type=int, default=64, help="Matrix height in elements") - parser.add_argument("--matrix_dim_n", type=int, default=64, help="Matrix width in elements") + parser.add_argument("--size_m", type=int, default=64, help="Matrix height in elements") + parser.add_argument("--size_n", type=int, default=64, help="Matrix width in elements") parser.add_argument("--output_dir", type=str, default="output", help="Directory for storing output files") args = parser.parse_args() @@ -296,16 +256,16 @@ def main(): READ_BASE_ADDR = args.read_base_addr WRITE_BASE_ADDR = args.write_base_addr TRANSP_MODE = args.transp_mode - MATRIX_DIM_C = args.num_channels - MATRIX_DIM_N = args.matrix_dim_n - MATRIX_DIM_M = args.matrix_dim_m - TOT_LENGTH = (args.matrix_dim_m * args.matrix_dim_n) // BANDWIDTH_ELEMS + TENSOR_SIZE_C = args.num_channels + TENSOR_SIZE_N = args.size_n + TENSOR_SIZE_M = args.size_m + TOT_LENGTH = (args.size_m * args.size_n) // BANDWIDTH_ELEMS OUTPUT_DIR = args.output_dir - if MEMORY_SIZE < ((MATRIX_DIM_C * MATRIX_DIM_N * MATRIX_DIM_M * ELEM_WIDTH // WORD_WIDTH) * 2): + if MEMORY_SIZE < ((TENSOR_SIZE_C * TENSOR_SIZE_N * TENSOR_SIZE_M * ELEM_WIDTH // WORD_WIDTH) * 2): raise ValueError(f"MEMORY_SIZE ({MEMORY_SIZE}) is too small for the given matrix size " - f"({MATRIX_DIM_C}x{MATRIX_DIM_M}x{MATRIX_DIM_N}) and element width ({ELEM_WIDTH})") + f"({TENSOR_SIZE_C}x{TENSOR_SIZE_M}x{TENSOR_SIZE_N}) and element width ({ELEM_WIDTH})") # num_elem_word must be power of two and greater than zero if args.num_elem_word & (args.num_elem_word - 1) != 0 or args.num_elem_word <= 0: raise ValueError("[GM] num_elem_word must be a power of two and greater than zero.") @@ -313,7 +273,7 @@ def main(): if BANDWIDTH_ALIGNED % WORD_WIDTH != 0: raise ValueError("[GM] BANDWIDTH_ALIGNED must be a multiple of the word size (num_elem_word * elem_width).") # # bandwidth width must be a multiple of word size - # if ((MATRIX_DIM_N * ELEM_WIDTH) < BANDWIDTH_ALIGNED): + # if ((TENSOR_SIZE_N * ELEM_WIDTH) < BANDWIDTH_ALIGNED): # raise ValueError("[GM] Matrix width (N) in bits must be at least as large as BANDWIDTH_ALIGNED.") # read_tot_length must not exceed 12-bit register capacity (4096) @@ -329,8 +289,8 @@ def main(): # transp_mode must be valid (1=1elem, 2=2elem, 4=4elem) if args.transp_mode not in [1, 2, 4]: raise ValueError("[GM] transp_mode must be 1 (1 elem), 2 (2 elem), or 4 (4 elem).") - if (MATRIX_DIM_N % args.transp_mode) != 0: - raise ValueError(f"[GM] Matrix width N ({MATRIX_DIM_N}) must be a multiple of transp_mode ({args.transp_mode}).") + if (TENSOR_SIZE_N % args.transp_mode) != 0: + raise ValueError(f"[GM] Matrix width N ({TENSOR_SIZE_N}) must be a multiple of transp_mode ({args.transp_mode}).") print(f"Memory Size: {MEMORY_SIZE} entries") print(f"Word Size: {WORD_WIDTH} bits") @@ -347,11 +307,11 @@ def main(): memory_flat = convert_memory_to_vector(memory, ELEM_WIDTH, WORD_WIDTH) # Extract matrix (read dimensions) from memory - input_matrix = [[0 for _ in range(MATRIX_DIM_N)] for _ in range(MATRIX_DIM_M)] - for d1 in range(MATRIX_DIM_M): + input_matrix = [[0 for _ in range(TENSOR_SIZE_N)] for _ in range(TENSOR_SIZE_M)] + for d1 in range(TENSOR_SIZE_M): row = [] - for d0 in range(MATRIX_DIM_N): - input_matrix[d1][d0] = memory_flat[(READ_BASE_ADDR + d1 * MATRIX_DIM_N + d0)] + for d0 in range(TENSOR_SIZE_N): + input_matrix[d1][d0] = memory_flat[(READ_BASE_ADDR + d1 * TENSOR_SIZE_N + d0)] # Print input matrix print("Input Matrix:") @@ -361,21 +321,23 @@ def main(): if args.datamover_mode == 0: # Copy mode output_matrix = input_matrix elif args.datamover_mode == 1: # Transpose mode - output_matrix = transpose(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, TRANSP_MODE) + output_matrix = transpose(input_matrix, TENSOR_SIZE_M, TENSOR_SIZE_N, TRANSP_MODE) elif args.datamover_mode == 2: # CIM mode - if args.cim_mode == 0: # row-major -> A-Layout - output_matrix = cim_layout(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, args.num_elem_word) - elif args.cim_mode == 1: # A-Layout -> row-major (use matrix dimenstions of original row-major layout) - output_matrix = cim_layout_reverse(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, args.num_elem_word) - elif args.datamover_mode == 3: # CIM layout transpose mode (INPUT SIZES EXPECTED IN ORIGINAL (ROW-MAJOR) LAYOUT FORM!) - input_matrix_chw = cim_layout_reverse(input_matrix, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, args.num_elem_word) - # Reshape input_matrix_chw to MATRIX_DIM_M x MATRIX_DIM_N + if args.cim_mode == 0: # row-major -> CIM-layout + output_matrix = cim_layout(input_matrix, TENSOR_SIZE_M, TENSOR_SIZE_N, args.row_tile_size, args.num_elem_word) + elif args.cim_mode == 1: # CIM-layout -> row-major (use matrix dimenstions of original row-major layout) + output_matrix = cim_layout_reverse(input_matrix, TENSOR_SIZE_M, TENSOR_SIZE_N, args.row_tile_size, args.num_elem_word) + elif args.datamover_mode == 3: # CIM-layout transpose mode (INPUT SIZES EXPECTED IN ORIGINAL (ROW-MAJOR) LAYOUT FORM!) + input_matrix_chw = cim_layout_reverse(input_matrix, TENSOR_SIZE_M, TENSOR_SIZE_N, args.row_tile_size, args.num_elem_word) + # Reshape input_matrix_chw to TENSOR_SIZE_M x TENSOR_SIZE_N input_matrix_chw_flat = [elem for row in input_matrix_chw for elem in row] - input_matrix_chw = [input_matrix_chw_flat[i * MATRIX_DIM_N:(i + 1) * MATRIX_DIM_N] for i in range(MATRIX_DIM_M)] - transposed_chw = transpose(input_matrix_chw, MATRIX_DIM_M, MATRIX_DIM_N, TRANSP_MODE) - transposed_chw_flat = [elem for row in transposed_chw for elem in row] - transposed_chw = [transposed_chw_flat[i * MATRIX_DIM_N:(i + 1) * MATRIX_DIM_N] for i in range(MATRIX_DIM_M)] - output_matrix = cim_layout(transposed_chw, MATRIX_DIM_M, MATRIX_DIM_N, args.cim_mode, args.cim_inner_dim, args.cim_outer_dim, args.num_elem_word) + input_matrix_chw = [input_matrix_chw_flat[i * TENSOR_SIZE_N:(i + 1) * TENSOR_SIZE_N] for i in range(TENSOR_SIZE_M)] + print("\nInput Matrix reshaped to CHW layout:") + print(input_matrix_chw) + transposed_chw = transpose(input_matrix_chw, TENSOR_SIZE_M, TENSOR_SIZE_N, TRANSP_MODE) + print("\nTransposed CHW Matrix:") + print(transposed_chw) + output_matrix = cim_layout(transposed_chw, TENSOR_SIZE_N, TENSOR_SIZE_M, args.row_tile_size, args.num_elem_word) else: raise ValueError("[GM] datamover_mode must be 0 (copy), 1 (transpose), or 2 (CIM).") @@ -409,11 +371,10 @@ def main(): 'datamover_mode': args.datamover_mode, 'transp_mode': args.transp_mode, 'cim_mode': args.cim_mode, - 'cim_inner_dim': args.cim_inner_dim, - 'cim_outer_dim': args.cim_outer_dim, - 'matrix_dim_m': args.matrix_dim_m, + 'row_tile_size': args.row_tile_size, + 'size_m': args.size_m, 'num_channels': args.num_channels, - 'matrix_dim_n': args.matrix_dim_n + 'size_n': args.size_n } write_data_header_file(OUTPUT_DIR, input_matrix, output_matrix, config_params) diff --git a/verif/python/validate_config.py b/verif/python/validate_config.py index 2532dec..d32b5d4 100755 --- a/verif/python/validate_config.py +++ b/verif/python/validate_config.py @@ -8,8 +8,8 @@ import argparse def validate_config(bandwidth, word_width, elem_width, memory_size, num_channels, - datamover_mode, transp_mode, cim_mode, cim_inner_dim, cim_outer_dim, - matrix_dim_m, matrix_dim_n): + datamover_mode, transp_mode, cim_mode, row_tile_size, + size_m, size_n): """Validate configuration parameters""" errors = [] warnings = [] @@ -27,13 +27,13 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, num_channels if word_width % elem_width != 0: errors.append(f"WORD_WIDTH ({word_width}) must be divisible by ELEM_WIDTH ({elem_width})") - if memory_size < (num_channels * matrix_dim_n * matrix_dim_m * elem_width // word_width) * 2: + if memory_size < (num_channels * size_n * size_m * elem_width // word_width) * 2: errors.append(f"MEMORY_SIZE ({memory_size}) is too small for the given matrix size " - f"({num_channels}x{matrix_dim_m}x{matrix_dim_n}) and element width ({elem_width})") + f"({num_channels}x{size_m}x{size_n}) and element width ({elem_width})") # Mode validation (based on config.mk) - if datamover_mode not in [0, 1, 2, 3]: - errors.append(f"DATAMOVER_MODE ({datamover_mode}) must be 0 (copy), 1 (transpose), 2 (CIM data layout conversion) or 3 (CIM layout transpose)") + if datamover_mode not in [0, 1, 2, 3, 4, 5]: + errors.append(f"DATAMOVER_MODE ({datamover_mode}) must be 0 (copy), 1 (transpose), 2 (CIM data layout conversion), 3 (CIM layout transpose), 4 (unfold), or 5 (fold)") # if transp_mode not in [0, 1, 2, 4]: # errors.append(f"TRANSP_MODE ({transp_mode}) must be 0, 1, 2, or 4") @@ -48,32 +48,28 @@ def validate_config(bandwidth, word_width, elem_width, memory_size, num_channels # CIM-specific validation if datamover_mode in [2, 3]: if cim_mode not in [0, 1]: - errors.append(f"CIM_MODE ({cim_mode}) must be 0 (row-major -> A-Layout) or 1 (A-Layout -> row-major) for CIM modes") - if (cim_inner_dim % bandwidth_elems != 0) and (cim_mode == 0): - errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) must be a multiple of bandwidth ({bandwidth_elems})") - if (cim_outer_dim % bandwidth_elems != 0) and (cim_mode == 1): - errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) must be a multiple of bandwidth ({bandwidth_elems})") - # if (cim_inner_dim > matrix_dim_n) and (cim_mode == 0): - # errors.append(f"CIM_INNER_DIM ({cim_inner_dim}) cannot be greater than matrix width ({matrix_dim_n})") - # if (cim_outer_dim > matrix_dim_m) and (cim_mode == 1): - # errors.append(f"CIM_OUTER_DIM ({cim_outer_dim}) cannot be greater than matrix height ({matrix_dim_m})") + errors.append(f"CIM_MODE ({cim_mode}) must be 0 (row-major -> CIM-layout) or 1 (CIM-layout -> row-major) for CIM modes") + if row_tile_size % bandwidth_elems != 0: + errors.append(f"ROW_TILE_SIZE ({row_tile_size}) must be a multiple of bandwidth ({bandwidth_elems})") + # if row_tile_size > size_n: + # errors.append(f"ROW_TILE_SIZE ({row_tile_size}) cannot be greater than matrix width ({size_n})") # Memory requirements - matrix_elements = num_channels * matrix_dim_m * matrix_dim_n + matrix_elements = num_channels * size_m * size_n matrix_words = (matrix_elements * elem_width + word_width - 1) // word_width total_memory_needed = matrix_words * 2 # Input + output matrices if total_memory_needed > memory_size: errors.append(f"Memory size ({memory_size} words) insufficient for matrices " - f"({total_memory_needed} words needed for {num_channels}x{matrix_dim_m}x{matrix_dim_n} input+output)") + f"({total_memory_needed} words needed for {num_channels}x{size_m}x{size_n} input+output)") # Matrix dimension alignment errors - # if matrix_dim_n % bandwidth_elems != 0: - # errors.append(f"Matrix width ({matrix_dim_n}) not aligned to bandwidth " + # if size_n % bandwidth_elems != 0: + # errors.append(f"Matrix width ({size_n}) not aligned to bandwidth " # f"({bandwidth_elems} elements)") - # if matrix_dim_m % bandwidth_elems != 0: - # errors.append(f"Matrix height ({matrix_dim_m}) not aligned to bandwidth " + # if size_m % bandwidth_elems != 0: + # errors.append(f"Matrix height ({size_m}) not aligned to bandwidth " # f"({bandwidth_elems} elements)") # Transpose-specific validation @@ -94,18 +90,17 @@ def main(): parser.add_argument("--datamover_mode", type=int, required=True) parser.add_argument("--transp_mode", type=int, required=True) parser.add_argument("--cim_mode", type=int, required=True) - parser.add_argument("--cim_inner_dim", type=int, required=True) - parser.add_argument("--cim_outer_dim", type=int, required=True) - parser.add_argument("--matrix_dim_m", type=int, required=True) - parser.add_argument("--matrix_dim_n", type=int, required=True) + parser.add_argument("--row_tile_size", type=int, required=True) + parser.add_argument("--size_m", type=int, required=True) + parser.add_argument("--size_n", type=int, required=True) args = parser.parse_args() errors, warnings = validate_config( args.bandwidth, args.word_width, args.elem_width, args.memory_size, args.num_channels, args.datamover_mode, args.transp_mode, args.cim_mode, - args.cim_inner_dim, args.cim_outer_dim, - args.matrix_dim_m, args.matrix_dim_n + args.row_tile_size, + args.size_m, args.size_n ) # Print results @@ -126,21 +121,20 @@ def main(): print("Configuration validation PASSED!") # Print mode information - mode_names = {0: "Copy", 1: "Transpose", 2: "CIM Data Layout Conversion"} - cim_mode_names = {0: "row-major -> A-Layout", 1: "row-major -> B-Layout"} + mode_names = {0: "Copy", 1: "Transpose", 2: "CIM Data Layout Conversion", 3: "CIM Layout Transpose", 4: "Unfold", 5: "Fold"} + cim_mode_names = {0: "row-major -> CIM-layout", 1: "CIM-layout -> row-major"} print(f"\nMode Configuration:") print(f" DATAMOVER_MODE: {args.datamover_mode} ({mode_names.get(args.datamover_mode, 'Unknown')})") print(f" TRANSP_MODE: {args.transp_mode}") print(f" CIM_MODE: {args.cim_mode} ({cim_mode_names.get(args.cim_mode, 'Unknown')})") if args.datamover_mode == 2: # CIM mode - print(f" CIM_INNER_DIM: {args.cim_inner_dim}") - print(f" CIM_OUTER_DIM: {args.cim_outer_dim}") + print(f" ROW_TILE_SIZE: {args.row_tile_size}") # Print computed values bandwidth_elems = args.bandwidth // args.elem_width num_elem_word = args.word_width // args.elem_width - matrix_words = (args.num_channels * args.matrix_dim_m * args.matrix_dim_n) // num_elem_word + matrix_words = (args.num_channels * args.size_m * args.size_n) // num_elem_word print(f"\nComputed values:") print(f" Channels: {args.num_channels}") diff --git a/verif/tb/tb_datamover_top_wrap.sv b/verif/tb/tb_datamover_top_wrap.sv index e09c7e5..b1753bd 100644 --- a/verif/tb/tb_datamover_top_wrap.sv +++ b/verif/tb/tb_datamover_top_wrap.sv @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 ETH Zurich and University of Bologna + * Copyright (C) 2025-2026 ETH Zurich and University of Bologna * * Copyright and related rights are licensed under the Solderpad Hardware * License, Version 0.51 (the "License"); you may not use this file except in @@ -17,6 +17,8 @@ * Cyrill Durrer */ +// OUTDATED! +// ToDo: Implement SW-based testing with a CPU core module tb_datamover_top_wrap; import datamover_package::*; @@ -64,8 +66,8 @@ import tb_package::*; logic [2:0] transp_mode; // logic [15:0] transp_len; - logic [11:0] matrix_dim_m; - logic [11:0] matrix_dim_n; + logic [11:0] tensor_size_m; + logic [11:0] tensor_size_n; logic [3:0] read_dim_enable; logic [3:0] write_dim_enable; logic [10:0] num_channels; @@ -129,8 +131,8 @@ import tb_package::*; assign transp_mode = `STIM_TRANSP_MODE; // assign transp_len = `STIM_TRANSP_LEN; - assign matrix_dim_m = `STIM_MATRIX_DIM_M; - assign matrix_dim_n = `STIM_MATRIX_DIM_N; + assign tensor_size_m = `STIM_TENSOR_SIZE_M; + assign tensor_size_n = `STIM_TENSOR_SIZE_N; assign read_dim_enable = `STIM_READ_DIM_ENABLE; assign write_dim_enable = `STIM_WRITE_DIM_ENABLE; assign num_channels = `STIM_NUM_CHANNELS; @@ -153,7 +155,7 @@ import tb_package::*; .ELEM_WIDTH ( ELEM_WIDTH ), .N_CORES ( N_CORES ), .N_CONTEXT ( 2 ), - .MISALIGNED_ACCESSES ( MISALIGNED_ACCESSES ) + .MISALIGNED_ACCESSES ( 0 ) ) i_hwpe_top_wrap ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), @@ -239,7 +241,7 @@ import tb_package::*; initial begin : main_execution logic [31:0] ctrl_engine_reg; - logic [31:0] matrix_dim_reg; + logic [31:0] tensor_dim_reg; logic [31:0] channels_reg; $info("Start execution...\n"); @@ -271,7 +273,7 @@ import tb_package::*; // Configure packed length registers (see datamover_package.sv) ctrl_engine_reg = {16'b0, write_dim_enable[3:0], read_dim_enable[3:0], 5'b0, transp_mode[2:0]}; - matrix_dim_reg = {matrix_dim_n[15:0], matrix_dim_m[15:0]}; + tensor_dim_reg = {tensor_size_n[15:0], tensor_size_m[15:0]}; channels_reg = {total_elements[20:0], num_channels[10:0]}; // Make sure tot_length is the same for read and write @@ -287,7 +289,7 @@ import tb_package::*; periph_write(datamover_package::DATAMOVER_REG_OUT_D2, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d2_stride, write_addr.d2_length}, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_OUT_D3, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d3_stride, write_addr.d3_length}, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_IN_OUT_D4_STRIDE, datamover_package::DATAMOVER_REGISTER_OFFS, {write_addr.d4_stride, read_addr.d4_stride}, clk_i, periph_bus); - periph_write(datamover_package::DATAMOVER_REG_MATRIX_DIM, datamover_package::DATAMOVER_REGISTER_OFFS, matrix_dim_reg, clk_i, periph_bus); + periph_write(datamover_package::DATAMOVER_REG_MATRIX_DIM, datamover_package::DATAMOVER_REGISTER_OFFS, tensor_dim_reg, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_CHANNELS, datamover_package::DATAMOVER_REGISTER_OFFS, channels_reg, clk_i, periph_bus); periph_write(datamover_package::DATAMOVER_REG_CTRL_ENGINE, datamover_package::DATAMOVER_REGISTER_OFFS, ctrl_engine_reg, clk_i, periph_bus); diff --git a/verif/tb/tb_package.sv b/verif/tb/tb_package.sv index b03b41c..49a8c4b 100644 --- a/verif/tb/tb_package.sv +++ b/verif/tb/tb_package.sv @@ -2,7 +2,7 @@ * tb_package.sv * Arpan Suravi Prasad * - * Copyright (C) 2018-2023 ETH Zurich, University of Bologna + * Copyright (C) 2018-2026 ETH Zurich, University of Bologna * Copyright and related rights are licensed under the Solderpad Hardware * License, Version 0.51 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at @@ -32,7 +32,6 @@ package tb_package; parameter BANDWIDTH = `BANDWIDTH; parameter NUM_ELEM_WORD = `NUM_ELEM_WORD; parameter ELEM_WIDTH = `ELEM_WIDTH; - parameter MISALIGNED_ACCESSES = `MISALIGNED_ACCESSES; parameter logic [2:0] TRANSP_MODE = `STIM_TRANSP_MODE; @@ -189,8 +188,8 @@ package tb_package; if (read_data !== golden_data) begin status = 1; $display("MISMATCH at address %0d: Expected %h, Actual %h", start_addr + i, golden_data, read_data); - end else if (i<100) begin//else begin - $display("MATCH at address %0d: %h", start_addr + i, read_data); + // end else begin + // $display("MATCH at address %0d: %h", start_addr + i, read_data); end end