diff --git a/.gitignore b/.gitignore index 1ded8d62..d47b993f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ my_init.sh vivado_* vivado.log vivado.jou - +examples/hello_world/.* +tags +*.pyc diff --git a/documentation/README.txt b/documentation/README.txt new file mode 100644 index 00000000..889b3c24 --- /dev/null +++ b/documentation/README.txt @@ -0,0 +1,20 @@ +These files were written by Marco Merlini (marco.merlini@utoronto.ca), who is +not the official maintainer of this repository. They are a summary of his +personal notes from using Galapagos. + +For Galapagos users: + + galapagos_flow.txt explains the design flow of using Galapagos + + +For Galapagos developers: + + file_reference.txt tries to disambiguate the utility of each script + packaged in this reposiitory + + adding_vivado_versions.txt gives a general method for extending Galapagos + to work with new versions of Vivado. Your mileage may vary. + + shell_development.txt explains how to add support for new boards in + Galapagos + diff --git a/documentation/galapagos_flow.txt b/documentation/galapagos_flow.txt new file mode 100644 index 00000000..8d83fca8 --- /dev/null +++ b/documentation/galapagos_flow.txt @@ -0,0 +1,122 @@ +=========================== +DEFINITIONS AND ASSUMPTIONS +=========================== + +In this file, $GALAPDIR will refer to the root directory of the Galapagos +repository. (Note: this is not an environment variable used by Galapagos; it is +a placeholder for you to replace yourself) + +$BOARD will refer to the name of the currently targeted board. + +All commands are assumed to be run from inside $GALAPDIR (unless otherwise +stated). + +A "kernel" means a single user module in the Galapagos cluster, analogous to a +Rank in MPI. + + +======== +OVERVIEW +======== + +The following numbered list explains the brief details of using Galapagos. More +details are provided in the later sections of this document. There is also a +quick blurb on writing kernels in HLS. + +When first installing Galapagos, + + 1) Run `$GALAPDIR/build.sh` and answer all its questions + +For each board you wish to use, + + 2) Use `galapagos_update_board ` to select the board, and + + 3) Run `make hlsmiddleware` from inside $GALAPDIR + +To create a Galapagos project, + + 4) Package kernels as IPs and place them in $GALAPDIR/hlsBuild/$BOARD/ip, + where $BOARD is the board that the IP is targeted for. + -> Use this directory even if your kernel is not written in HLS + + 5) Write a logical file and a mapping file (details provided below) + + 6) Set the LOGICALFILE and MAPFILE environment variables to be the full + path of your logical file and mapping file (repsectively). Also set the + PROJECTNAME environment variable to be the desired project name + + 7) Run `make middleware` from inside $GALAPDIR + + 8) If there were no errors, Galapagos will have created a directory called + $GALAPDIR/projects/$PROJECTNAME which contains a number of TCL scripts + and a bash script called createCluster.sh. Simply run this shell script + and wait for Vivado to produce your bitstream(s). + + +=============================== +WHEN FIRST INSTALLING GALAPAGOS +=============================== + +For the time being, Galapagos works by reading a number of environment +variables. Run + + $ cd $GALAPDIR + $ source build.sh + +This will bring up a snazzy menu. Answer its questions. Don't worry about +making a mistake; you can always try this again if it doesn't work. + +Once you have finished answering the questions, Galapgos will have created a +new file in your home folder. You can take a look at it: + + $ less ~/.galapagos + +This file simply sets Galapagos's environment variables using the answers you +provided earlier. It also defines two bash shell functions: +galapagos-update-board and galapagos-update-version. The first function allows +you ask Galapagos to target a different board without re-running build.sh, and +the second lets you select a different Vivado version. + +Finally, note that build.sh adds a line to your .bashrc: + + $ less ~/.bashrc + +This ensures that .galapagos is called whenever you open a new bash shell. + + +============================== +FOR EACH BOARD YOU PLAN TO USE +============================== + +Galapagos uses a number of custom IP cores written in HLS. These must be +synthesized and packaged as IPs before you can start making your own projects. + +Run the following from inside $GALAPDIR: + + $ galapagos-update-board + $ make hlsmiddleware + +Currently, Galapagos supports the following choices for : + + - zynq-p2 + - zedboard + - sidewinder + - adm-8k5 + - adm-8k5-debug + + +====================== +WRITING KERNELS IN HLS +====================== + +Include the header file in $GALAPDIR/middleware/include/galapagos_packet.h + +The following is a skeleton of an HLS Galapagos kernel: + + (TODO) + + +===================== +CREATING YOUR PROJECT +===================== + diff --git a/examples/hello_world/Makefile b/examples/hello_world/Makefile new file mode 100644 index 00000000..340e1f80 --- /dev/null +++ b/examples/hello_world/Makefile @@ -0,0 +1,43 @@ +all: hello_world world middleware pick_up_vivados_dirty_socks + +hello_world: .hello_world +.hello_world: hello_world.cpp + vivado_hls hello_world.tcl + touch .hello_world + +world: .world +.world: world.cpp + vivado_hls world.tcl + touch .world + +export LOGICALFILE=$(shell realpath logical.xml) +export MAPFILE=$(shell realpath mapping.xml) +export PROJECTNAME=hello_world + +middleware: .middleware +.middleware: logical.xml mapping.xml .hello_world .world + make -C$(GALAPAGOS_PATH) middleware + touch .middleware + +pick_up_vivados_dirty_socks: + # Cleaning up after Vivado... + rm -rf vivado* + +show_reports: + less $(GALAPAGOS_PATH)/hlsBuild/$(GALAPAGOS_BOARD_NAME)/ip/hello_world/solution1/syn/report/hello_world_csynth.rpt + less $(GALAPAGOS_PATH)/hlsBuild/$(GALAPAGOS_BOARD_NAME)/ip/world/solution1/syn/report/world_csynth.rpt + +clean: + rm -rf $(GALAPAGOS_PATH)/hlsBuild/$(GALAPAGOS_BOARD_NAME)/ip/hello_world/ + rm -rf $(GALAPAGOS_PATH)/hlsBuild/$(GALAPAGOS_BOARD_NAME)/ip/world/ + rm -rf $(GALAPAGOS_PATH)/projects/hello_world + rm -rf .world + rm -rf .hello_world + rm -rf .middleware + rm -rf vivado* + + +# Very bizarre! GNU Make will silently add calls to g++ if you have cpp files +# in your directory (even if none of your rules or dependencies mention it). +# This is because of some implicit rules... 30 minutes I found this solution +.SUFFIXES: diff --git a/examples/hello_world/README.txt b/examples/hello_world/README.txt new file mode 100644 index 00000000..650dbaa0 --- /dev/null +++ b/examples/hello_world/README.txt @@ -0,0 +1,26 @@ +===================== +GALAPAGOS HELLO WORLD +===================== + +This example contains: + + - hello_world.cpp, a Galapagos HLS kernel with DEST = 1 that contantly + broadcasts a message to the kernel with DEST = 2 + + - world.cpp, a Galapagos HLS kernel with DEST = 2 that ignores all + incoming messages + + - hello_world.tcl and world.tcl. Scripts that invoke Vivado HLS to compile + hello_world.cpp and world.cpp into Verilog + + - logical.xml, the logical file + + - mapping.xml, the mapping file + + - A Makefile + + +Make sure you have followed the Galapagos setup instructions (see +documentation/galapagos_flow.txt). This Makefile will generate a Vivado project +in projects/hello_world/0/ + diff --git a/examples/hello_world/hello_world.cpp b/examples/hello_world/hello_world.cpp new file mode 100644 index 00000000..4abf1445 --- /dev/null +++ b/examples/hello_world/hello_world.cpp @@ -0,0 +1,20 @@ +#include "galapagos_packet.h" + +using gp = galapagos::stream_packet<64>; + +void hello_world(hls::stream *out) { + #pragma HLS INTERFACE axis register both port=out + #pragma HLS INTERFACE ap_ctrl_none port=return + + gp tmp; + + tmp.dest = 2; + tmp.data = 0xFEEDBADBEEF2BABE; + tmp.id = 1; + tmp.last = 1; + tmp.keep = 0xFFFF; + + if(!out->full()) out->write(tmp); + + +} diff --git a/examples/hello_world/hello_world.tcl b/examples/hello_world/hello_world.tcl new file mode 100644 index 00000000..a737ad73 --- /dev/null +++ b/examples/hello_world/hello_world.tcl @@ -0,0 +1,23 @@ +set galapagos_path $::env(GALAPAGOS_PATH) +set board_name $::env(GALAPAGOS_BOARD_NAME) +set part_name $::env(GALAPAGOS_PART) + +set src_path_root $galapagos_path/examples/hello_world + +# Does this still work? We'll find out, I guess... +cd $galapagos_path/hlsBuild/${board_name}/ip + +open_project hello_world +set_top hello_world +open_solution "solution1" +set_part ${part_name} +#csynth path +add_files $src_path_root/hello_world.cpp -cflags "-I $galapagos_path/middleware/include -I $galapagos_path/middleware/CPP_lib/Galapagos_lib" +create_clock -period 250MHz -name default +config_interface -expose_global +csynth_design +export_design -format ip_catalog +close_project + +quit + diff --git a/examples/hello_world/logical.xml b/examples/hello_world/logical.xml new file mode 100644 index 00000000..a135f519 --- /dev/null +++ b/examples/hello_world/logical.xml @@ -0,0 +1,51 @@ + + + + + 64 + 1 + 1 + 16 + + + + + hello_world + + xilinx.com + hls + 1.0 + 1 + 1 + + + ap_clk + ap_rst_n + + global + out_r + + + + + world + + xilinx.com + hls + 1.0 + 2 + 1 + + + ap_clk + ap_rst_n + + global + in_r + + + + diff --git a/examples/hello_world/mapping.xml b/examples/hello_world/mapping.xml new file mode 100644 index 00000000..3ccc85fb --- /dev/null +++ b/examples/hello_world/mapping.xml @@ -0,0 +1,17 @@ + + + + + hw + raw + sidewinder + 01:23:45:67:89:AB + 10.10.84.234 + + + 1 + 2 + + + + diff --git a/examples/hello_world/world.cpp b/examples/hello_world/world.cpp new file mode 100644 index 00000000..6129ebe5 --- /dev/null +++ b/examples/hello_world/world.cpp @@ -0,0 +1,11 @@ +#include "galapagos_packet.h" + +using gp = galapagos::stream_packet<64>; + +void world(hls::stream *in) { + #pragma HLS INTERFACE axis register both port=in + #pragma HLS INTERFACE ap_ctrl_none port=return + + gp tmp; + if (!in->empty()) in->read(tmp); +} diff --git a/examples/hello_world/world.tcl b/examples/hello_world/world.tcl new file mode 100644 index 00000000..9b0cbfa2 --- /dev/null +++ b/examples/hello_world/world.tcl @@ -0,0 +1,23 @@ +set galapagos_path $::env(GALAPAGOS_PATH) +set board_name $::env(GALAPAGOS_BOARD_NAME) +set part_name $::env(GALAPAGOS_PART) + +set src_path_root $galapagos_path/examples/hello_world + +# Does this still work? We'll find out, I guess... +cd $galapagos_path/hlsBuild/${board_name}/ip + +open_project world +set_top world +open_solution "solution1" +set_part ${part_name} +#csynth path +add_files $src_path_root/world.cpp -cflags "-I $galapagos_path/middleware/include -I $galapagos_path/middleware/CPP_lib/Galapagos_lib" +create_clock -period 250MHz -name default +config_interface -expose_global +csynth_design +export_design -format ip_catalog +close_project + +quit + diff --git a/middleware/Makefile b/middleware/Makefile index ae4a3bc0..b1b18ac2 100644 --- a/middleware/Makefile +++ b/middleware/Makefile @@ -24,7 +24,7 @@ python_path = $(middleware_path)/python all: middleware hlsmiddleware middleware: guard-LOGICALFILE guard-MAPFILE guard-PROJECTNAME ${LOGICALFILE} ${MAPFILE} - python3.5 ${python_path}/globalFPGAParser.py --logicalFile=${LOGICALFILE} \ + python ${python_path}/globalFPGAParser.py --logicalFile=${LOGICALFILE} \ --mapFile=${MAPFILE} --projectName=${PROJECTNAME} chmod +x $(GALAPAGOS_PATH)/projects/$(PROJECTNAME)/createCluster.sh diff --git a/middleware/hls/axis_unconcat/Makefile b/middleware/hls/axis_unconcat/Makefile new file mode 100644 index 00000000..318635d2 --- /dev/null +++ b/middleware/hls/axis_unconcat/Makefile @@ -0,0 +1,38 @@ +# Edit the following four variables and run `make`. +# After that, follow the instructions in ip_maker.tcl +# +# Alternatively, you could set any of these variables on the command line: +# +# $ make src_dir=/path/to/my/src dst_dir=/path/to/my/output +# +# Just make sure that you don't include a trailing slash on your directories + +dst_dir=$(GALAPAGOS_PATH)/hlsBuild/$(GALAPAGOS_BOARD_NAME)/ip +src_dir=. +ip_name=axis_unconcat +part_no=$(GALAPAGOS_PART) + + +# Makes Makefile easier to read +out_dir=${dst_dir}/${ip_name} + +default: ip + +clean: + rm -rf ${out_dir} + rm -rf ${ip_name}_tmp_proj + +# Packages into a Vivado IP +ip: clean + rm -rf ${out_dir} + mkdir -p ${out_dir}/src + cp axis_unconcat.v ../marcos_macros/macros.vh ${out_dir}/src + vivado -nolog -nojournal -notrace -mode batch -source ip_maker.tcl -tclargs ${out_dir} ${ip_name} ${part_no} + rm -rf ${ip_name}_tmp_proj + rm -f *log + rm -rf .Xil + rm -f vivado* + +syntax_check: + iverilog -DICARUS_VERILOG -I../marcos_macros -E $(MODULE).v + rm -rf a.out diff --git a/middleware/hls/axis_unconcat/axis_unconcat.v b/middleware/hls/axis_unconcat/axis_unconcat.v new file mode 100644 index 00000000..4367fcca --- /dev/null +++ b/middleware/hls/axis_unconcat/axis_unconcat.v @@ -0,0 +1,198 @@ +`timescale 1ns / 1ps + +/* + +The counterpart to axis_concat.v + + +Each side channel has an "in enable" and an "out enable". This means: + +in | out | Meaning +------------------------------------ + d | 0 | The side channel is not present at the output + 0 | 1 | The side channel is split out of output TDATA + 1 | 1 | The side channel is present at input, and not split out of output TDATA + +*/ + +`include "macros.vh" + +/* + +Unfortunately, due to problems with Vivado, I have to move all the +automatically derived parameters into macros instead of using localparam + +Specifically, this happens because you can't use localparam to set a module's +port width, even if you use the K&R-style Verilog syntax: + + module my_thing # ( + parameter W = 2 + ) (a, b); + localparam WW = W+W; + input wire [W -1:0] a; + output wire [WW -1:0] b; + + endmodule + +Vivado chokes on it. + +So there is no choice but to use ugly `define statements... and to make things +worse, you can't do + x ? 1 : 0 +Vivado only understands + (x != 0) ? 1 : 0 +*/ + +//Derived parameters. Do not set manually! +// +//The "SPLIT_*_WIDTH" parameters mean how many additional bits are present in +//the output TDATA +// +//The "SAFE_*_WIDTH" parameters are an ugly hack. See, if I just did +//something like +// input wire [DEST_WIDTH - 1 :0] +//but DEST_WIDTH was 0, then we would get a Verilog error. Although I plan +//to have the packaged IP hide the ability to edit the width if you disable +//the port, I don't want obscure errors if the user typed in 0 before +//disabling. +// +//The "PASSTHRU_*" indicate that a side channel shouldnot be concatted with +//TDATA; instead, it is given its own output +// +`define SPLIT_LAST (((IN_ENABLE_LAST == 0) && (OUT_ENABLE_LAST != 0)) ? 1 : 0) +`define SPLIT_KEEP (((IN_ENABLE_KEEP == 0) && (OUT_ENABLE_KEEP != 0)) ? 1 : 0) +`define SPLIT_DEST (((IN_ENABLE_DEST == 0) && (OUT_ENABLE_DEST != 0)) ? 1 : 0) +`define SPLIT_ID (((IN_ENABLE_ID == 0) && (OUT_ENABLE_ID != 0)) ? 1 : 0) +`define SPLIT_USER (((IN_ENABLE_USER == 0) && (OUT_ENABLE_USER != 0)) ? 1 : 0) + +`define SPLIT_LAST_WIDTH (`SPLIT_LAST) //For consistency + +`define KEEP_WIDTH ((DATA_WIDTH+7)/8) +`define SPLIT_KEEP_WIDTH (`SPLIT_KEEP * `KEEP_WIDTH) +`define IN_SAFE_KEEP_WIDTH ((IN_ENABLE_KEEP != 0) ? `KEEP_WIDTH : 1) +`define OUT_SAFE_KEEP_WIDTH ((OUT_ENABLE_KEEP != 0) ? `KEEP_WIDTH : 1) + +`define SPLIT_DEST_WIDTH (`SPLIT_DEST * DEST_WIDTH) +`define IN_SAFE_DEST_WIDTH ((IN_ENABLE_DEST != 0) ? DEST_WIDTH : 1) +`define OUT_SAFE_DEST_WIDTH ((OUT_ENABLE_DEST != 0) ? DEST_WIDTH : 1) + +`define SPLIT_ID_WIDTH (`SPLIT_ID * ID_WIDTH) +`define IN_SAFE_ID_WIDTH ((IN_ENABLE_ID != 0) ? ID_WIDTH : 1) +`define OUT_SAFE_ID_WIDTH ((OUT_ENABLE_ID != 0) ? ID_WIDTH : 1) + +`define SPLIT_USER_WIDTH (`SPLIT_USER * USER_WIDTH) +`define IN_SAFE_USER_WIDTH ((IN_ENABLE_USER != 0) ? USER_WIDTH : 1) +`define OUT_SAFE_USER_WIDTH ((OUT_ENABLE_USER != 0) ? USER_WIDTH : 1) + + +`define PASSTHRU_LAST (((IN_ENABLE_LAST != 0) && (OUT_ENABLE_LAST != 0)) ? 1 : 0) +`define PASSTHRU_KEEP (((IN_ENABLE_KEEP != 0) && (OUT_ENABLE_KEEP != 0)) ? 1 : 0) +`define PASSTHRU_DEST (((IN_ENABLE_DEST != 0) && (OUT_ENABLE_DEST != 0)) ? 1 : 0) +`define PASSTHRU_ID (((IN_ENABLE_ID != 0) && (OUT_ENABLE_ID != 0)) ? 1 : 0) +`define PASSTHRU_USER (((IN_ENABLE_USER != 0) && (OUT_ENABLE_USER != 0)) ? 1 : 0) + +`define WIDTH_U (`SPLIT_USER_WIDTH) +`define WIDTH_IU (`SPLIT_ID_WIDTH + `WIDTH_U) +`define WIDTH_DIU (`SPLIT_DEST_WIDTH + `WIDTH_IU) +`define WIDTH_KDIU (`SPLIT_KEEP_WIDTH + `WIDTH_DIU) +`define WIDTH_LKDIU (`SPLIT_LAST_WIDTH + `WIDTH_KDIU) +`define WIDTH_DLKDIU (DATA_WIDTH + `WIDTH_LKDIU) + +module axis_unconcat # ( + parameter DATA_WIDTH = 32, + + parameter IN_ENABLE_KEEP = 0, + parameter OUT_ENABLE_KEEP = 0, + + parameter IN_ENABLE_LAST = 1, + parameter OUT_ENABLE_LAST = 1, + + parameter IN_ENABLE_DEST = 0, + parameter OUT_ENABLE_DEST = 0, + parameter DEST_WIDTH = 16, + + parameter IN_ENABLE_ID = 0, + parameter OUT_ENABLE_ID = 0, + parameter ID_WIDTH = 16, + + parameter IN_ENABLE_USER = 0, + parameter OUT_ENABLE_USER = 0, + parameter USER_WIDTH = 16 +) ( + input wire clk, //Dummy clock to get rid of Vivado's annoying warning + + input wire [DATA_WIDTH + + `SPLIT_LAST_WIDTH + + `SPLIT_KEEP_WIDTH + + `SPLIT_DEST_WIDTH + + `SPLIT_ID_WIDTH + + `SPLIT_USER_WIDTH + -1:0] left_TDATA, + input wire left_TVALID, + output wire left_TREADY, + input wire left_TLAST, + input wire [`IN_SAFE_KEEP_WIDTH -1:0] left_TKEEP, + input wire [`IN_SAFE_DEST_WIDTH -1:0] left_TDEST, + input wire [`IN_SAFE_ID_WIDTH -1:0] left_TID, + input wire [`IN_SAFE_USER_WIDTH -1:0] left_TUSER, + + output wire [DATA_WIDTH-1:0] right_TDATA, + output wire right_TVALID, + input wire right_TREADY, + output wire right_TLAST, + output wire [`OUT_SAFE_KEEP_WIDTH -1:0] right_TKEEP, + output wire [`OUT_SAFE_DEST_WIDTH -1:0] right_TDEST, + output wire [`OUT_SAFE_ID_WIDTH -1:0] right_TID, + output wire [`OUT_SAFE_USER_WIDTH -1:0] right_TUSER +); + +assign right_TDATA = left_TDATA[`WIDTH_DLKDIU -1 -: DATA_WIDTH]; + +`genif (`SPLIT_LAST) begin + assign right_TLAST = left_TDATA[`WIDTH_LKDIU -1 -: 1]; +`endgen + +`genif (`SPLIT_KEEP) begin + assign right_TKEEP = left_TDATA[`WIDTH_KDIU -1 -: `KEEP_WIDTH]; +`endgen + +`genif (`SPLIT_DEST) begin + assign right_TDEST = left_TDATA[`WIDTH_DIU -1 -: DEST_WIDTH]; +`endgen + +`genif (`SPLIT_ID) begin + assign right_TID = left_TDATA[`WIDTH_IU -1 -: ID_WIDTH]; +`endgen + +`genif (`SPLIT_USER) begin + assign right_TUSER = left_TDATA[`WIDTH_U -1 -: USER_WIDTH]; +`endgen + +assign right_TVALID = left_TVALID; +assign left_TREADY = right_TREADY; + +`genif (`PASSTHRU_LAST) begin + assign right_TLAST = left_TLAST; +`endgen + +`genif (`PASSTHRU_KEEP) begin + assign right_TKEEP = left_TKEEP; +`endgen + +`genif (`PASSTHRU_DEST) begin + assign right_TDEST = left_TDEST; +`endgen + +`genif (`PASSTHRU_ID) begin + assign right_TID = left_TID; +`endgen + +`genif (`PASSTHRU_USER) begin + assign right_TUSER = left_TUSER; +`endgen + + + + +endmodule + diff --git a/middleware/hls/axis_unconcat/ip_maker.tcl b/middleware/hls/axis_unconcat/ip_maker.tcl new file mode 100644 index 00000000..e4ef0755 --- /dev/null +++ b/middleware/hls/axis_unconcat/ip_maker.tcl @@ -0,0 +1,170 @@ +# Call as: +# vivado -mode tcl -nolog -nojournal -source scripts/ip_package.tcl -tclargs $out_dir $ip_name $part_name + +set out_dir [lindex $argv 0] +set ip_name [lindex $argv 1] +set part_name [lindex $argv 2] +set project_name ${ip_name}_tmp_proj +create_project ${project_name} ${project_name} -part ${part_name} +add_files ${out_dir}/src +ipx::package_project -root_dir ${out_dir} -vendor mmerlini -library yov -taxonomy /UserIP + +# Set conditional portds in AXI Streams. I hope this plays nicely with the rest +# of Vivado! +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.IN_ENABLE_ID')) = 1} [ipx::get_ports left_TID -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.IN_ENABLE_DEST')) = 1} [ipx::get_ports left_TDEST -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.IN_ENABLE_KEEP')) = 1} [ipx::get_ports left_TKEEP -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.IN_ENABLE_LAST')) = 1} [ipx::get_ports left_TLAST -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.IN_ENABLE_USER')) = 1} [ipx::get_ports left_TUSER -of_objects [ipx::current_core]] +set_property driver_value 0 [ipx::get_ports right_TID -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.OUT_ENABLE_ID')) = 1} [ipx::get_ports right_TID -of_objects [ipx::current_core]] +set_property driver_value 0 [ipx::get_ports right_TDEST -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.OUT_ENABLE_DEST')) = 1} [ipx::get_ports right_TDEST -of_objects [ipx::current_core]] +set_property driver_value 0 [ipx::get_ports right_TKEEP -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.OUT_ENABLE_KEEP')) = 1} [ipx::get_ports right_TKEEP -of_objects [ipx::current_core]] +set_property driver_value 0 [ipx::get_ports right_TLAST -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.OUT_ENABLE_LAST')) = 1} [ipx::get_ports right_TLAST -of_objects [ipx::current_core]] +set_property driver_value 0 [ipx::get_ports right_TUSER -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.OUT_ENABLE_USER')) = 1} [ipx::get_ports right_TUSER -of_objects [ipx::current_core]] + +# OUT_ENABLE_LAST +set_property display_name {Enable TLAST output} [ipgui::get_guiparamspec -name "OUT_ENABLE_LAST" -component [ipx::current_core] ] +set_property tooltip {} [ipgui::get_guiparamspec -name "OUT_ENABLE_LAST" -component [ipx::current_core] ] +set_property widget {checkBox} [ipgui::get_guiparamspec -name "OUT_ENABLE_LAST" -component [ipx::current_core] ] +set_property value false [ipx::get_user_parameters OUT_ENABLE_LAST -of_objects [ipx::current_core]] +set_property value false [ipx::get_hdl_parameters OUT_ENABLE_LAST -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_user_parameters OUT_ENABLE_LAST -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_hdl_parameters OUT_ENABLE_LAST -of_objects [ipx::current_core]] + +# OUT_ENABLE_KEEP +set_property display_name {Enable TKEEP output} [ipgui::get_guiparamspec -name "OUT_ENABLE_KEEP" -component [ipx::current_core] ] +set_property tooltip {Enable TKEEP output} [ipgui::get_guiparamspec -name "OUT_ENABLE_KEEP" -component [ipx::current_core] ] +set_property widget {checkBox} [ipgui::get_guiparamspec -name "OUT_ENABLE_KEEP" -component [ipx::current_core] ] +set_property value false [ipx::get_user_parameters OUT_ENABLE_KEEP -of_objects [ipx::current_core]] +set_property value false [ipx::get_hdl_parameters OUT_ENABLE_KEEP -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_user_parameters OUT_ENABLE_KEEP -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_hdl_parameters OUT_ENABLE_KEEP -of_objects [ipx::current_core]] + +# OUT_ENABLE_DEST +set_property display_name {Enable TDEST output} [ipgui::get_guiparamspec -name "OUT_ENABLE_DEST" -component [ipx::current_core] ] +set_property tooltip {Enable TDEST output} [ipgui::get_guiparamspec -name "OUT_ENABLE_DEST" -component [ipx::current_core] ] +set_property widget {checkBox} [ipgui::get_guiparamspec -name "OUT_ENABLE_DEST" -component [ipx::current_core] ] +set_property value false [ipx::get_user_parameters OUT_ENABLE_DEST -of_objects [ipx::current_core]] +set_property value false [ipx::get_hdl_parameters OUT_ENABLE_DEST -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_user_parameters OUT_ENABLE_DEST -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_hdl_parameters OUT_ENABLE_DEST -of_objects [ipx::current_core]] + +# OUT_ENABLE_ID +set_property display_name {Enable TID output} [ipgui::get_guiparamspec -name "OUT_ENABLE_ID" -component [ipx::current_core] ] +set_property tooltip {Enable TID output} [ipgui::get_guiparamspec -name "OUT_ENABLE_ID" -component [ipx::current_core] ] +set_property widget {checkBox} [ipgui::get_guiparamspec -name "OUT_ENABLE_ID" -component [ipx::current_core] ] +set_property value false [ipx::get_user_parameters OUT_ENABLE_ID -of_objects [ipx::current_core]] +set_property value false [ipx::get_hdl_parameters OUT_ENABLE_ID -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_user_parameters OUT_ENABLE_ID -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_hdl_parameters OUT_ENABLE_ID -of_objects [ipx::current_core]] + +# OUT_ENABLE_USER +set_property display_name {Enable TUSER output} [ipgui::get_guiparamspec -name "OUT_ENABLE_USER" -component [ipx::current_core] ] +set_property tooltip {Enable TUSER output} [ipgui::get_guiparamspec -name "OUT_ENABLE_USER" -component [ipx::current_core] ] +set_property widget {checkBox} [ipgui::get_guiparamspec -name "OUT_ENABLE_USER" -component [ipx::current_core] ] +set_property value false [ipx::get_user_parameters OUT_ENABLE_USER -of_objects [ipx::current_core]] +set_property value false [ipx::get_hdl_parameters OUT_ENABLE_USER -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_user_parameters OUT_ENABLE_USER -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_hdl_parameters OUT_ENABLE_USER -of_objects [ipx::current_core]] + +# IN_ENABLE_LAST +set_property display_name {TLAST source} [ipgui::get_guiparamspec -name "IN_ENABLE_LAST" -component [ipx::current_core] ] +set_property tooltip {} [ipgui::get_guiparamspec -name "IN_ENABLE_LAST" -component [ipx::current_core] ] +set_property widget {comboBox} [ipgui::get_guiparamspec -name "IN_ENABLE_LAST" -component [ipx::current_core] ] +set_property value 0 [ipx::get_user_parameters IN_ENABLE_LAST -of_objects [ipx::current_core]] +set_property value 0 [ipx::get_hdl_parameters IN_ENABLE_LAST -of_objects [ipx::current_core]] +set_property value_validation_type pairs [ipx::get_user_parameters IN_ENABLE_LAST -of_objects [ipx::current_core]] +set_property value_validation_pairs {{Input TLAST} 1 {Split out of TDATA} 0} [ipx::get_user_parameters IN_ENABLE_LAST -of_objects [ipx::current_core]] +set_property enablement_tcl_expr {$OUT_ENABLE_LAST != 0} [ipx::get_user_parameters IN_ENABLE_LAST -of_objects [ipx::current_core]] + +# IN_ENABLE_KEEP +set_property display_name {TKEEP source} [ipgui::get_guiparamspec -name "IN_ENABLE_KEEP" -component [ipx::current_core] ] +set_property tooltip {} [ipgui::get_guiparamspec -name "IN_ENABLE_KEEP" -component [ipx::current_core] ] +set_property widget {comboBox} [ipgui::get_guiparamspec -name "IN_ENABLE_KEEP" -component [ipx::current_core] ] +set_property value 0 [ipx::get_user_parameters IN_ENABLE_KEEP -of_objects [ipx::current_core]] +set_property value 0 [ipx::get_hdl_parameters IN_ENABLE_KEEP -of_objects [ipx::current_core]] +set_property value_validation_type pairs [ipx::get_user_parameters IN_ENABLE_KEEP -of_objects [ipx::current_core]] +set_property value_validation_pairs {{Input TKEEP} 1 {Split out of TDATA} 0} [ipx::get_user_parameters IN_ENABLE_KEEP -of_objects [ipx::current_core]] +set_property enablement_tcl_expr {$OUT_ENABLE_KEEP != 0} [ipx::get_user_parameters IN_ENABLE_KEEP -of_objects [ipx::current_core]] + +# IN_ENABLE_DEST +set_property display_name {TDEST source} [ipgui::get_guiparamspec -name "IN_ENABLE_DEST" -component [ipx::current_core] ] +set_property tooltip {} [ipgui::get_guiparamspec -name "IN_ENABLE_DEST" -component [ipx::current_core] ] +set_property widget {comboBox} [ipgui::get_guiparamspec -name "IN_ENABLE_DEST" -component [ipx::current_core] ] +set_property value 0 [ipx::get_user_parameters IN_ENABLE_DEST -of_objects [ipx::current_core]] +set_property value 0 [ipx::get_hdl_parameters IN_ENABLE_DEST -of_objects [ipx::current_core]] +set_property value_validation_type pairs [ipx::get_user_parameters IN_ENABLE_DEST -of_objects [ipx::current_core]] +set_property value_validation_pairs {{Input TDEST} 1 {Split out of TDATA} 0} [ipx::get_user_parameters IN_ENABLE_DEST -of_objects [ipx::current_core]] +set_property enablement_tcl_expr {$OUT_ENABLE_DEST != 0} [ipx::get_user_parameters IN_ENABLE_DEST -of_objects [ipx::current_core]] + +# IN_ENABLE_ID +set_property display_name {TID source} [ipgui::get_guiparamspec -name "IN_ENABLE_ID" -component [ipx::current_core] ] +set_property tooltip {} [ipgui::get_guiparamspec -name "IN_ENABLE_ID" -component [ipx::current_core] ] +set_property widget {comboBox} [ipgui::get_guiparamspec -name "IN_ENABLE_ID" -component [ipx::current_core] ] +set_property value 0 [ipx::get_user_parameters IN_ENABLE_ID -of_objects [ipx::current_core]] +set_property value 0 [ipx::get_hdl_parameters IN_ENABLE_ID -of_objects [ipx::current_core]] +set_property value_validation_type pairs [ipx::get_user_parameters IN_ENABLE_ID -of_objects [ipx::current_core]] +set_property value_validation_pairs {{Input TID} 1 {Split out of TDATA} 0} [ipx::get_user_parameters IN_ENABLE_ID -of_objects [ipx::current_core]] +set_property enablement_tcl_expr {$OUT_ENABLE_ID != 0} [ipx::get_user_parameters IN_ENABLE_ID -of_objects [ipx::current_core]] + +# IN_ENABLE_USER +set_property display_name {TUSER source} [ipgui::get_guiparamspec -name "IN_ENABLE_USER" -component [ipx::current_core] ] +set_property tooltip {} [ipgui::get_guiparamspec -name "IN_ENABLE_USER" -component [ipx::current_core] ] +set_property widget {comboBox} [ipgui::get_guiparamspec -name "IN_ENABLE_USER" -component [ipx::current_core] ] +set_property value 0 [ipx::get_user_parameters IN_ENABLE_USER -of_objects [ipx::current_core]] +set_property value 0 [ipx::get_hdl_parameters IN_ENABLE_USER -of_objects [ipx::current_core]] +set_property value_validation_type pairs [ipx::get_user_parameters IN_ENABLE_USER -of_objects [ipx::current_core]] +set_property value_validation_pairs {{Input TUSER} 1 {Split out of TDATA} 0} [ipx::get_user_parameters IN_ENABLE_USER -of_objects [ipx::current_core]] +set_property enablement_tcl_expr {$OUT_ENABLE_USER != 0} [ipx::get_user_parameters IN_ENABLE_USER -of_objects [ipx::current_core]] + +# DEST_WIDTH +set_property enablement_tcl_expr {$OUT_ENABLE_DEST != 0} [ipx::get_user_parameters DEST_WIDTH -of_objects [ipx::current_core]] + +# ID_WIDTH +set_property enablement_tcl_expr {$OUT_ENABLE_ID != 0} [ipx::get_user_parameters ID_WIDTH -of_objects [ipx::current_core]] + +# USER WIDTH +set_property enablement_tcl_expr {$OUT_ENABLE_USER != 0} [ipx::get_user_parameters USER_WIDTH -of_objects [ipx::current_core]] + +# Get the GUI menu to look nice +ipgui::add_group -name {TLAST} -component [ipx::current_core] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core] ] -display_name {TLAST} +ipgui::move_group -component [ipx::current_core] -order 1 [ipgui::get_groupspec -name "TLAST" -component [ipx::current_core]] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "IN_ENABLE_LAST" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TLAST" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "OUT_ENABLE_LAST" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TLAST" -component [ipx::current_core]] +ipgui::add_group -name {TKEEP} -component [ipx::current_core] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core] ] -display_name {TKEEP} +ipgui::move_group -component [ipx::current_core] -order 1 [ipgui::get_groupspec -name "TKEEP" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TLAST" -component [ipx::current_core]] +ipgui::move_group -component [ipx::current_core] -order 2 [ipgui::get_groupspec -name "TKEEP" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TLAST" -component [ipx::current_core]] +ipgui::move_group -component [ipx::current_core] -order 2 [ipgui::get_groupspec -name "TKEEP" -component [ipx::current_core]] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "OUT_ENABLE_KEEP" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TKEEP" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "IN_ENABLE_KEEP" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TKEEP" -component [ipx::current_core]] +ipgui::add_group -name {TDEST} -component [ipx::current_core] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core] ] -display_name {TDEST} +ipgui::move_group -component [ipx::current_core] -order 3 [ipgui::get_groupspec -name "TDEST" -component [ipx::current_core]] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "OUT_ENABLE_DEST" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TDEST" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 1 [ipgui::get_guiparamspec -name "IN_ENABLE_KEEP" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TKEEP" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "IN_ENABLE_DEST" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TDEST" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 1 [ipgui::get_guiparamspec -name "IN_ENABLE_DEST" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TDEST" -component [ipx::current_core]] +ipgui::add_group -name {TID} -component [ipx::current_core] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core] ] -display_name {TID} +ipgui::move_group -component [ipx::current_core] -order 4 [ipgui::get_groupspec -name "TID" -component [ipx::current_core]] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "IN_ENABLE_ID" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TID" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "ID_WIDTH" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TID" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "OUT_ENABLE_ID" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TID" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 1 [ipgui::get_guiparamspec -name "DEST_WIDTH" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TDEST" -component [ipx::current_core]] +ipgui::add_group -name {TUSER} -component [ipx::current_core] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core] ] -display_name {TUSER} +ipgui::move_group -component [ipx::current_core] -order 5 [ipgui::get_groupspec -name "TUSER" -component [ipx::current_core]] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "OUT_ENABLE_USER" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TUSER" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "IN_ENABLE_USER" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TUSER" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 1 [ipgui::get_guiparamspec -name "IN_ENABLE_USER" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TUSER" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "USER_WIDTH" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TUSER" -component [ipx::current_core]] +ipgui::move_param -component [ipx::current_core] -order 1 [ipgui::get_guiparamspec -name "USER_WIDTH" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "TUSER" -component [ipx::current_core]] + + +ipx::create_xgui_files [ipx::current_core] +ipx::update_checksums [ipx::current_core] +ipx::save_core [ipx::current_core] +close_project +exit diff --git a/middleware/hls/buffered_handshake/Makefile b/middleware/hls/buffered_handshake/Makefile new file mode 100644 index 00000000..b53bee61 --- /dev/null +++ b/middleware/hls/buffered_handshake/Makefile @@ -0,0 +1,53 @@ +# Edit the following four variables and run `make`. +# After that, follow the instructions in ip_maker.tcl +# +# Alternatively, you could set any of these variables on the command line: +# +# $ make src_dir=/path/to/my/src dst_dir=/path/to/my/output +# +# Just make sure that you don't include a trailing slash on your directories + +dst_dir=$(GALAPAGOS_PATH)/hlsBuild/$(GALAPAGOS_BOARD_NAME)/ip +src_dir=. +ip_name=bhand +part_no=$(GALAPAGOS_PART) + +# Makes Makefile easier to read +out_dir=${dst_dir}/${ip_name} + +MODULE := bhand + +# By default, package as IP +default: ip + +tb: $(MODULE).vcd + +$(MODULE).vcd: $(MODULE).vvp + vvp $(MODULE).vvp + +$(MODULE).vvp: $(MODULE).v $(MODULE)_tb.v $(MODULE)_drivers.mem + iverilog -DICARUS_VERILOG -o $(MODULE).vvp $(MODULE)_tb.v + +open: $(MODULE).vcd + gtkwave $(MODULE).vcd --autosavename & + +clean: + rm -rf $(MODULE).vvp + rm -rf $(MODULE).vcd + rm -rf ${out_dir} + rm -rf ${ip_name}_tmp_proj + +# Packages into a Vivado IP +ip: clean + rm -rf ${out_dir} + mkdir -p ${out_dir}/src + cp bhand.v ${out_dir}/src + vivado -nolog -nojournal -notrace -mode batch -source ip_maker.tcl -tclargs ${out_dir} ${ip_name} ${part_no} + rm -rf ${ip_name}_tmp_proj + rm -f *log + rm -rf .Xil + rm -f vivado* + +force: + touch $(MODULE)_tb.v + make diff --git a/middleware/hls/buffered_handshake/README.txt b/middleware/hls/buffered_handshake/README.txt new file mode 100644 index 00000000..7bb4fae3 --- /dev/null +++ b/middleware/hls/buffered_handshake/README.txt @@ -0,0 +1,507 @@ +=============================== +WHAT IS "BUFFERED HANDSHAKING"? +=============================== + +This is a term I learned from Dan Gisselquist: + + https://zipcpu.com/blog/2017/08/14/strategies-for-pipelining.html + +Some friends of mine mentioned that they learned a similar technique in their +undergrad comp arch class. However, a Google search for "buffered handshake" +doesn't turn up anything other than the article I just linked, so the technique +must have a different name elsewhere. + +Basically, when you have a pipelined design that needs to handle backpressure, +you need a way to propagate that pressure backwards. If you're not careful and +just have a global (i.e. combinational) ready signal, then you can fail timing. +Buffered handshaking is a way of addressing this problem. + +Note: this is exactly the same thing as an AXI Stream register slice in one of +the modes you can choose. + +======= +DETAILS +======= + +The general idea is something like this: we will delay data and valid in the +forward direction, and delay ready in the backward direction: + + +-----------+ +-----------+ +-----------+ + ------->|data data|------->|data data|------->|data data|-------> + ------->|vld vld|------->|vld vld|------->|vld vld|-------> + <-------|rdy rdy|<-------|rdy rdy|<-------|rdy rdy|<------- + |> | |> | |> | + +-----------+ +-----------+ +-----------+ + A B C + +Here's the problem: consider stage B. If its RHS ready goes low, it will take +one cycle before its LHS ready goes low. That means that B needs a _second +register_ in order to save the incoming data _without overwriting_ the data +that is already inside it. + +The next few plates (mostly copied from Dan Gisselquist's article and converted +to use ready+valid signalling) will illustrate the general idea. + + +------------------------------------------------ +-----------------------------------------+ + | Legend | +t = 0: Initial state. The value "a" is input. |-----------------------------------------| + | | + +-+ +-+ +-+ | +-----------------------+ | + | | | | | | | |Data saved in extra reg| | + +-+ ---> |-| |-| |-| | ---> |-----------------------| ---> vld | + |a| <--- | | <--- | | <--- | | <--- | <--- |Data for this stage | <--- rdy | + +-+ +-+ +-+ +-+ | +-----------------------+ | + Input 1 2 3 | | + | If arrow is present, signal is asserted | +------------------------------------------------ +-----------------------------------------+ +t = 1: "a" was accepted, and "b" is input. + + +-+ +-+ +-+ + | | | | | | + +-+ ---> |-| ---> |-| |-| + |b| <--- |a| <--- | | <--- | | <--- + +-+ +-+ +-+ +-+ + Input 1 2 3 +------------------------------------------------------------------------------ +t = 2: "b" was accepted, and "c" is input. + + +-+ +-+ +-+ + | | | | | | + +-+ ---> |-| ---> |-| ---> |-| + |c| <--- |b| <--- |a| <--- | | <--- + +-+ +-+ +-+ +-+ + Input 1 2 3 +------------------------------------------------------------------------------ +t = 3: "c" was accepted, and "d" is input. Because RHS ready and valid are +high, the value "a" will be output on this cycle. + + +-+ +-+ +-+ + | | | | | | + +-+ ---> |-| ---> |-| ---> |-| ---> + |d| <--- |c| <--- |b| <--- |a| <--- + +-+ +-+ +-+ +-+ + Input 1 2 3 +------------------------------------------------------------------------------ +t = 3: "d" was accepted, and "e" is input. RHS ready went low, so "b" will not +be output. However, note that stage 3's ready is still high; this means c must +be saved, since stage 2 will discard it on this cycle. + + +-+ +-+ +-+ + | | | | | | + +-+ ---> |-| ---> |-| ---> |-| ---> + |e| <--- |d| <--- |c| <--- |b| + +-+ +-+ +-+ +-+ + Input 1 2 3 +------------------------------------------------------------------------------ +t = 4: "e" was accepted, and "f" is input. RHS ready still low, so "b" will not +be output. Now, the low ready signal has propagated backwards through stage 3, +so "d" will not be read. However, note that stage 2's ready is still high; this +means e must be saved, since stage 1 will discard it on this cycle. + + +-+ +-+ +-+ + | | | | |c| + +-+ ---> |-| ---> |-| ---> |-| ---> + |f| <--- |e| <--- |d| |b| + +-+ +-+ +-+ +-+ + Input 1 2 3 +------------------------------------------------------------------------------ +t = 5: "f" was accepted, and "g" is input. RHS ready went high again, so "b" +will be output. Now, the low ready signals have propagated backwards through +stages 2 and 3, so "d" and "f" will not be read. However, note that stage 1's +ready is still high; this means g must be saved, since the input will discard +it on this cycle. + + +-+ +-+ +-+ + | | |e| |c| + +-+ ---> |-| ---> |-| ---> |-| ---> + |g| <--- |f| |d| |b| <--- + +-+ +-+ +-+ +-+ + Input 1 2 3 +------------------------------------------------------------------------------ +t = 6: "g" was accepted, and "h" is input. RHS ready is still high, so "c" will +be output. Now, the low ready signals have propagated backwards through stages +1 and 2, so "f" and "h" will not be read. At this point, the input side is +receiving backpressure. + + +-+ +-+ +-+ + |g| |e| | | + +-+ ---> |-| ---> |-| ---> |-| ---> + |h| |f| |d| <--- |c| <--- + +-+ +-+ +-+ +-+ + Input 1 2 3 +------------------------------------------------------------------------------ +t = 7: RHS ready is still high, so "d" will be output. The low ready signals +have propagated backwards through stages 1 and 2, so "f" and "h" will not be +read. At this point, the input side is receiving backpressure. + + +-+ +-+ +-+ + |g| | | | | + +-+ ---> |-| ---> |-| ---> |-| ---> + |h| |f| <--- |e| <--- |d| <--- + +-+ +-+ +-+ +-+ + Input 1 2 3 +------------------------------------------------------------------------------ +t = 8: RHS ready is still high, so "d" will be output. All the low ready +signals have "bubbled out" of the pipeline, and we're back to normal operation. +Note that the output ready was low for two cycles, and (after a delay) the +input was also low for two cycles. This is no accident. + + +-+ +-+ +-+ + | | | | | | + +-+ ---> |-| ---> |-| ---> |-| ---> + |h| <--- |g| <--- |f| <--- |e| <--- + +-+ +-+ +-+ +-+ + Input 1 2 3 +------------------------------------------------------------------------------ + + +============== +IMPLEMENTATION +============== + +A buffered handshake can really be thought of as a two-element FIFO: + + +-----------+ + ------->|data data|------> + ------->|vld vld|------> + <-------|rdy rdy|<------ + |> | + +-----------+ + +A good question is "why can't we use a one-element FIFO?" The answer is because +a one-element FIFO will have a combinational path between the right-hand ready +signal and the left-hand ready signal. This happens when the FIFO is full: if +the output side is ready (meaning the FIFO's value will be read on this cycle), +then the input side is ready (meaning a new value can enter), but if the output +side is not ready, then the input side is not ready. + + That explanation is not 100% complete: you could make the same argument + about a two-element FIFO as well (i.e. when it is full, a combinational + path exists between right-side ready and left-side ready). We actually do + not permit a write into the FIFO when it is full _even if a value will exit + on this cycle_. + + One last thing: why can't we do this with a one-element FIFO (i.e. + disallow inputting when full)? You can, but then it is impossible to + maintain line rate, since the single element will have to be filled and + emptied for every data beat. + +Because of the (small) fixed FIFO size, we don't need to keep track of read and +write pointers. Instead, we'll do something a little more ad-hoc: start by +making a one-element FIFO and add a few things until we're done. + +One-element FIFO +---------------- + +This is about as simple as I can make it: + + module fifo_single ( + input wire clk, + + input wire [7:0] idata, + input wire idata_vld, + output wire idata_rdy, + + output wire [7:0] odata, + output wire odata_vld, + input wire odata_rdy + ); + + //Some helper signals for neatening up the code + wire shift_in; + assign shift_in = idata_vld && idata_rdy; + + wire shift_out; + assign shift_out = odata_vld && odata_rdy; + + + + //Internal registers and signals for FIFO element + reg [7:0] mem = 0; + reg mem_vld = 0; + wire mem_rdy; + + //We are ready if FIFO is empty, or if the value is leaving on this cycle + assign mem_rdy = !mem_vld || (odata_vld && odata_rdy); + + //We will enable writing into mem if it is ready, and if the input is valid + wire mem_en; + assign mem_en = mem_rdy && idata_vld; + + always @(posedge clk) begin + //mem's next value + if (mem_en) begin + mem <= idata; + end + + //mem_vld's next value + if (mem_en) begin + mem_vld <= 1; + end else if (shift_out) begin + mem_vld <= 0; + end + end + + + + //Actually wire up module outputs + assign idata_rdy = mem_rdy; + assign odata = mem; + assign odata_vld = mem_vld; + + endmodule + +Verilog is so hard to read... I'm not really sure what to do about it... + + +Converting into a buffered handshake +------------------------------------ + +We're pretty much already finished. We only need to make four changes: + + - Add a second FIFO element + - Load second element when original element is full + - Allow loading the original element from the second element (when needed) + - Calculate the idata_rdy signal based on whether the extra register is free + +The text below is the buffered handshake module. Lines beginning with '#' are +modifications of the fifo_single module, and lines beginning with '>' are +additions. (Note: a two-column diff is provided below; this is only here in +case you prefer using a narrow screen) + + #module buffered_handshake ( + input wire clk, + + input wire [7:0] idata, + input wire idata_vld, + output wire idata_rdy, + + output wire [7:0] odata, + output wire odata_vld, + input wire odata_rdy + ); + + //Some helper signals for neatening up the code + wire shift_in; + assign shift_in = idata_vld && idata_rdy; + + wire shift_out; + assign shift_out = odata_vld && odata_rdy; + + + + > //Forward-declare this signal since extra_mem needs it + > reg mem_vld = 0; + > + > + > + > //Internal registers and signals for extra element + > reg [7:0] extra_mem = 0; + > reg extra_mem_vld = 0; + > wire extra_mem_rdy; + > + > //Unlike a regular FIFO, we are only ready if empty: + > assign extra_mem_rdy = (extra_mem_vld == 0); + > + > //We will enable writing into extra mem if a new element is shifting in AND + > //mem is full AND mem will not be read on this cycle + > wire extra_mem_en; + > assign extra_mem_en = shift_in && mem_vld && !shift_out; + > + > always @(posedge clk) begin + > //extra_mem's next value + > if (extra_mem_en) begin + > extra_mem <= idata; + > end + > + > //extra_mem_vld's next value + > if (extra_mem_en) begin + > extra_mem_vld <= 1; + > end else if (shift_out) begin + > extra_mem_vld <= 0; + > end + > end + > + > + > + //Internal registers and signals for FIFO element + reg [7:0] mem = 0; + # //reg mem_vld = 0; //moved + wire mem_rdy; + + //We are ready if FIFO is empty, or if the value is leaving on this cycle + assign mem_rdy = !mem_vld || (odata_vld && odata_rdy); + + //We will enable writing into mem if it is ready, and if the input is valid + wire mem_en; + # assign mem_en = mem_rdy && (idata_vld || extra_mem_vld); + + always @(posedge clk) begin + //mem's next value + if (mem_en) begin + # mem <= extra_mem_vld ? extra_mem : idata; + end + + //mem_vld's next value + if (mem_en) begin + mem_vld <= 1; + end else if (shift_out) begin + mem_vld <= 0; + end + end + + + + //Actually wire up module outputs + # assign idata_rdy = extra_mem_rdy; + assign odata = mem; + assign odata_vld = mem_vld; + + endmodule + + + + + + +As promised, here is the two-column diff: + + module fifo_single ( # module buffered_handshake ( + input wire clk, + + input wire [7:0] idata, + input wire idata_vld, + output wire idata_rdy, + + output wire [7:0] odata, + output wire odata_vld, + input wire odata_rdy + ); + + //Some helper signals for neatening up the code + wire shift_in; + assign shift_in = idata_vld && idata_rdy; + + wire shift_out; + assign shift_out = odata_vld && odata_rdy; + + + + > //Forward-declare this signal since extra_mem needs it + > reg mem_vld = 0; + > + > + > + > //Internal registers and signals for extra element + > reg [7:0] extra_mem = 0; + > reg extra_mem_vld = 0; + > wire extra_mem_rdy; + > + > //Unlike a regular FIFO, we are only ready if empty: + > assign extra_mem_rdy = (extra_mem_vld == 0); + > + > //We will enable writing into extra mem if a new element is shifting in AND + > //mem is full AND mem will not be read on this cycle + > wire extra_mem_en; + > assign extra_mem_en = shift_in && mem_vld && !shift_out; + > + > always @(posedge clk) begin + > //extra_mem's next value + > if (extra_mem_en) begin + > extra_mem <= idata; + > end + > + > //extra_mem_vld's next value + > if (extra_mem_en) begin + > extra_mem_vld <= 1; + > end else if (shift_out) begin + > extra_mem_vld <= 0; + > end + > end + > + > + > + //Internal registers and signals for FIFO element + reg [7:0] mem = 0; + reg mem_vld = 0; # //reg mem_vld = 0; //moved + wire mem_rdy; + + //We are ready if FIFO is empty, or if the value is leaving on this cycle + assign mem_rdy = !mem_vld || (odata_vld && odata_rdy); + + //We will enable writing into mem if it is ready, and if the input is valid + wire mem_en; + assign mem_en = mem_rdy && idata_vld; # assign mem_en = mem_rdy && (idata_vld || extra_mem_vld); + + always @(posedge clk) begin + //mem's next value + if (mem_en) begin + mem <= idata; # mem <= extra_mem_vld ? extra_mem : idata; + end + + //mem_vld's next value + if (mem_en) begin + mem_vld <= 1; + end else if (shift_out) begin + mem_vld <= 0; + end + end + + + + //Actually wire up module outputs + assign idata_rdy = mem_rdy; # assign idata_rdy = extra_mem_rdy; + assign odata = mem; + assign odata_vld = mem_vld; + + endmodule + + +============= +COUNTING MODE +============= + +One last thing: in some cases, I need to count how many cycles a value has been +in a pipeline. To do this, I can use almost the exact same code, except every +time I assign a value to mem or extra_mem, I increment it first (not forgetting +the case when the value doesn't change!) + +So, taking the extra_mem code, + + always @(posedge clk) begin + //extra_mem's next value + if (extra_mem_en) begin + extra_mem <= idata; + end + end + +we simply change it to say: + + always @(posedge clk) begin + //extra_mem's next value + if (extra_mem_en) begin + extra_mem <= idata + 1; + end else begin + extra_mem <= extra_mem + 1; + end + +And similarly for the mem code, take + + always @(posedge clk) begin + //mem's next value + if (mem_en) begin + mem <= extra_mem_vld ? extra_mem : idata; + end + end + +and change it to + + always @(posedge clk) begin + //mem's next value + if (mem_en) begin + mem <= extra_mem_vld ? extra_mem + 1 : idata + 1; + end else begin + mem <= mem + 1; + end + end diff --git a/middleware/hls/buffered_handshake/bhand.gtkw b/middleware/hls/buffered_handshake/bhand.gtkw new file mode 100644 index 00000000..c4eeca02 --- /dev/null +++ b/middleware/hls/buffered_handshake/bhand.gtkw @@ -0,0 +1,47 @@ +[*] +[*] GTKWave Analyzer v3.3.102 (w)1999-2019 BSI +[*] Thu Nov 14 15:54:21 2019 +[*] +[dumpfile] "/home/mahkoe/research/fpga-bpf/sources/generic/buffered_handshake/bhand.vcd" +[dumpfile_mtime] "Thu Nov 14 15:52:51 2019" +[dumpfile_size] 4742 +[savefile] "/home/mahkoe/research/fpga-bpf/sources/generic/buffered_handshake/bhand.gtkw" +[timestart] 0 +[size] 1916 1060 +[pos] -1 -1 +*-15.632915 335000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] bhand_tb. +[treeopen] bhand_tb.DUT. +[sst_width] 214 +[signals_width] 174 +[sst_expanded] 1 +[sst_vpaned_height] 610 +@28 +bhand_tb.DUT.clk +bhand_tb.rst +@200 +-input side +@22 +bhand_tb.idata[7:0] +@28 +bhand_tb.idata_vld +bhand_tb.idata_rdy +@200 +-output_side +@22 +bhand_tb.odata[7:0] +@28 +bhand_tb.odata_vld +bhand_tb.odata_rdy +@23 +bhand_tb.DUT.ocount[3:0] +@200 +-bhand internals +@22 +bhand_tb.DUT.extra_mem[7:0] +bhand_tb.DUT.mem[7:0] +@28 +bhand_tb.DUT.extra_mem_vld +bhand_tb.DUT.mem_vld +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/middleware/hls/buffered_handshake/bhand.v b/middleware/hls/buffered_handshake/bhand.v new file mode 100644 index 00000000..3b4f31fb --- /dev/null +++ b/middleware/hls/buffered_handshake/bhand.v @@ -0,0 +1,197 @@ +`ifndef BHAND_INCLUDE_GUARD +`define BHAND_INCLUDE_GUARD 1 + +`timescale 1ns / 1ps +/* +bhand.v + +Implements a buffered handshake. Can control whether or not the core has reset +signals and of which polarity. (Unfortunately, it makes the Verilog harder to +read...) + +*/ + +`define genif generate if +`define else_genif end else if +`define endgen end endgenerate + +`define NO_RESET 0 +`define ACTIVE_HIGH 1 +`define ACTIVE_LOW 2 + +module bhand # ( + parameter DATA_WIDTH = 8, + parameter RESET_TYPE = `ACTIVE_HIGH +) ( + input wire clk, + //Reset is used according to the RESET_TYPE parameter + input wire rst, + + //Connect your input stream signals here + //For example, + // .idata({in_TDATA, in_TKEEP, in_TDEST, in_TID, in_TLAST}), + // .idata_vld(in_TVALID), + // .idata_rdy(in_TREADY) + // + //Keep in mind that idata_rdy is an output of this module + input wire [DATA_WIDTH-1:0] idata, + input wire idata_vld, + output wire idata_rdy, + + //Connect your output stream signals here + //For example, + // .odata({out_TDATA, out_TKEEP, out_TDEST, out_TID, out_TLAST}), + // .odata_vld(out_TVALID), + // .odata_rdy(out_TREADY) + // + //Keep in mind that odata_rdy is an input of this module + output wire [DATA_WIDTH-1:0] odata, + output wire odata_vld, + input wire odata_rdy +); + + //Some helper signals for neatening up the code + wire shift_in; + assign shift_in = idata_vld && idata_rdy; + + wire shift_out; + assign shift_out = odata_vld && odata_rdy; + + //Forward-declare this signal since extra_mem needs it + reg mem_vld = 0; + + //Internal registers and signals for extra element + reg [DATA_WIDTH-1:0] extra_mem = 0; + reg extra_mem_vld = 0; + wire extra_mem_rdy; + + //Unlike a regular FIFO, we are only ready if empty: + assign extra_mem_rdy = (extra_mem_vld == 0); + + //We will enable writing into extra mem if a new element is shifting in AND + //mem is full AND mem will not be read on this cycle + wire extra_mem_en; + assign extra_mem_en = shift_in && mem_vld && !shift_out; + +`genif (RESET_TYPE == `NO_RESET) begin + + always @(posedge clk) begin + //extra_mem_vld's next value + if (extra_mem_en) begin + extra_mem_vld <= 1; + end else if (shift_out) begin + extra_mem_vld <= 0; + end + end + +`else_genif (RESET_TYPE == `ACTIVE_HIGH) begin + + always @(posedge clk) begin + if (rst) begin + extra_mem_vld <= 0; + end else begin + if (extra_mem_en) begin + extra_mem_vld <= 1; + end else if (shift_out) begin + extra_mem_vld <= 0; + end + end + end + +`else_genif (RESET_TYPE == `ACTIVE_LOW) begin + + always @(posedge clk) begin + if (!rst) begin + extra_mem_vld <= 0; + end else begin + if (extra_mem_en) begin + extra_mem_vld <= 1; + end else if (shift_out) begin + extra_mem_vld <= 0; + end + end + end + +`endgen + + //extra_mem's next value + always @(posedge clk) begin + if (extra_mem_en) begin + extra_mem <= idata; + end + end + + + //Internal registers and signals for FIFO element + reg [DATA_WIDTH-1:0] mem = 0; + //reg mem_vld = 0; //moved + wire mem_rdy; + + //We are ready if FIFO is empty, or if the value is leaving on this cycle + assign mem_rdy = !mem_vld || (odata_vld && odata_rdy); + + //We will enable writing into mem if it is ready, and if the input is valid + wire mem_en; + assign mem_en = mem_rdy && (idata_vld || extra_mem_vld); + +`genif (RESET_TYPE == `NO_RESET) begin + always @(posedge clk) begin + //mem_vld's next value + if (mem_en) begin + mem_vld <= 1; + end else if (shift_out) begin + mem_vld <= 0; + end + end +end else if (RESET_TYPE == `ACTIVE_HIGH) begin + always @(posedge clk) begin + //mem_vld's next value + if (rst) begin + mem_vld <= 0; + end else begin + if (mem_en) begin + mem_vld <= 1; + end else if (shift_out) begin + mem_vld <= 0; + end + end + end +end else if (RESET_TYPE == `ACTIVE_LOW) begin + always @(posedge clk) begin + //mem_vld's next value + if (!rst) begin + mem_vld <= 0; + end else begin + if (mem_en) begin + mem_vld <= 1; + end else if (shift_out) begin + mem_vld <= 0; + end + end + end +`endgen + + always @(posedge clk) begin + //mem's next value + if (mem_en) begin + mem <= extra_mem_vld ? extra_mem : idata; + end + end + + //Actually wire up module outputs + assign idata_rdy = extra_mem_rdy; + assign odata = mem; + assign odata_vld = mem_vld; + +endmodule + + +`undef NO_RESET +`undef ACTIVE_HIGH +`undef ACTIVE_LOW + +`undef genif +`undef else_genif +`undef endgen + +`endif diff --git a/middleware/hls/buffered_handshake/bhand_drivers.mem b/middleware/hls/buffered_handshake/bhand_drivers.mem new file mode 100644 index 00000000..94847706 --- /dev/null +++ b/middleware/hls/buffered_handshake/bhand_drivers.mem @@ -0,0 +1,35 @@ +idata idata_vld odata_rdy rst +00 0 1 0 +DE 1 1 0 +AD 1 1 0 +00 0 1 0 +BE 1 1 0 +EF 1 1 0 +00 0 0 0 +FE 1 0 0 +ED 1 1 0 +ED 1 1 0 +BE 1 1 0 +00 0 1 0 +EF 1 1 0 +00 0 1 0 +00 0 1 1 +00 0 1 0 +DE 1 1 0 +AD 1 1 1 +00 0 1 0 +BE 1 1 0 +00 0 0 0 +00 0 0 0 +00 0 0 0 +00 0 0 0 +EF 1 1 0 +00 0 0 0 +FE 1 0 0 +ED 1 1 0 +ED 1 1 0 +BE 1 1 0 +00 0 1 0 +EF 1 1 0 +00 0 1 0 +00 0 1 0 diff --git a/middleware/hls/buffered_handshake/bhand_tb.v b/middleware/hls/buffered_handshake/bhand_tb.v new file mode 100644 index 00000000..4db21a7c --- /dev/null +++ b/middleware/hls/buffered_handshake/bhand_tb.v @@ -0,0 +1,81 @@ +`timescale 1ns / 1ps + +/* +bhand_tb.v + +A testbench for the buffered handshake +*/ + +`ifdef ICARUS_VERILOG +`include "bhand.v" +`endif + +`define DATA_WIDTH 8 +`define COUNT_WIDTH 4 +`define ENABLE_COUNT 1 + +module bhand_tb; + reg clk; + reg rst; + reg [`DATA_WIDTH-1:0] idata; + reg idata_vld; + wire idata_rdy; + wire [`DATA_WIDTH-1:0] odata; + wire odata_vld; + reg odata_rdy; + + integer fd, dummy; + + initial begin + $dumpfile("bhand.vcd"); + $dumpvars; + $dumplimit(512000); + + clk <= 0; + rst <= 0; + idata <= 0; + idata_vld <= 0; + odata_rdy <= 0; + + fd = $fopen("bhand_drivers.mem", "r"); + if (fd == 0) begin + $display("Could not open file"); + $finish; + end + + while ($fgetc(fd) != "\n") begin + if ($feof(fd)) begin + $display("Error: file is in incorrect format"); + $finish; + end + end + end + + always #5 clk <= ~clk; + + always @(posedge clk) begin + if ($feof(fd)) begin + $display("Reached end of drivers file"); + #20 + $finish; + end + + #0.01 + dummy = $fscanf(fd, "%x%b%b%b", idata, idata_vld, odata_rdy, rst); + end + + bhand # ( + .DATA_WIDTH(`DATA_WIDTH), + .RESET_TYPE(1) + ) DUT ( + .clk(clk), + .rst(rst), + .idata(idata), + .idata_vld(idata_vld), + .idata_rdy(idata_rdy), + .odata(odata), + .odata_vld(odata_vld), + .odata_rdy(odata_rdy) + ); + +endmodule diff --git a/middleware/hls/buffered_handshake/ip_maker.tcl b/middleware/hls/buffered_handshake/ip_maker.tcl new file mode 100644 index 00000000..72521dd0 --- /dev/null +++ b/middleware/hls/buffered_handshake/ip_maker.tcl @@ -0,0 +1,43 @@ +# Call as: +# vivado -mode tcl -nolog -nojournal -source scripts/ip_package.tcl -tclargs $out_dir $ip_name $part_name + +set out_dir [lindex $argv 0] +set ip_name [lindex $argv 1] +set part_name [lindex $argv 2] +set project_name ${ip_name}_tmp_proj +create_project ${project_name} ${project_name} -part ${part_name} +add_files ${out_dir}/src +ipx::package_project -root_dir ${out_dir} -vendor mmerlini -library yov -taxonomy /UserIP + +# Fix up 'rst' interface +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.RESET_TYPE')) = 1} [ipx::get_bus_interfaces rst -of_objects [ipx::current_core]] +ipx::add_bus_parameter POLARITY [ipx::get_bus_interfaces rst -of_objects [ipx::current_core]] +set_property VALUE ACTIVE_HIGH [ipx::get_bus_parameters POLARITY -of_objects [ipx::get_bus_interfaces rst -of_objects [ipx::current_core]]] + +# Add 'rstn' interface +ipx::add_bus_interface rstn [ipx::current_core] +set_property abstraction_type_vlnv xilinx.com:signal:reset_rtl:1.0 [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property bus_type_vlnv xilinx.com:signal:reset:1.0 [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.RESET_TYPE')) = 2} [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +ipx::add_port_map RST [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property physical_name rst [ipx::get_port_maps RST -of_objects [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]]] +ipx::add_bus_parameter POLARITY [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property VALUE ACTIVE_LOW [ipx::get_bus_parameters POLARITY -of_objects [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]]] + +# Do up 'RESET_TYPE' configuration parameter +set_property display_name {Reset type} [ipgui::get_guiparamspec -name "RESET_TYPE" -component [ipx::current_core] ] +set_property tooltip {} [ipgui::get_guiparamspec -name "RESET_TYPE" -component [ipx::current_core] ] +set_property widget {comboBox} [ipgui::get_guiparamspec -name "RESET_TYPE" -component [ipx::current_core] ] +set_property value 0 [ipx::get_user_parameters RESET_TYPE -of_objects [ipx::current_core]] +set_property value 0 [ipx::get_hdl_parameters RESET_TYPE -of_objects [ipx::current_core]] +set_property value_validation_type pairs [ipx::get_user_parameters RESET_TYPE -of_objects [ipx::current_core]] +set_property value_validation_pairs {None 0 {Active high} 1 {Active low} 2} [ipx::get_user_parameters RESET_TYPE -of_objects [ipx::current_core]] + +ipx::create_xgui_files [ipx::current_core] +ipx::update_checksums [ipx::current_core] +ipx::save_core [ipx::current_core] +close_project +exit + + + diff --git a/middleware/hls/dbg_guv/Makefile b/middleware/hls/dbg_guv/Makefile new file mode 100644 index 00000000..8008a40f --- /dev/null +++ b/middleware/hls/dbg_guv/Makefile @@ -0,0 +1,61 @@ +# Edit the following four variables and run `make`. +# After that, follow the instructions in ip_maker.tcl +# +# Alternatively, you could set any of these variables on the command line: +# +# $ make src_dir=/path/to/my/src dst_dir=/path/to/my/output +# +# Just make sure that you don't include a trailing slash on your directories + +dst_dir=$(GALAPAGOS_PATH)/hlsBuild/$(GALAPAGOS_BOARD_NAME)/ip +src_dir=. +ip_name=dbg_guv +part_no=$(GALAPAGOS_PART) + + + +# Makes Makefile easier to read +out_dir=${dst_dir}/${ip_name} + +# These variables are for running the testbench +MODULE := dbg_guv + +# By default, package as an ip +default: ip + +# This is to run the testbench +tb: $(MODULE).vcd + +# This opens the testbench in gtkwave +open: $(MODULE).vcd + gtkwave $(MODULE).vcd --autosavename & + +# Compile the Verilog into Icarus's special format +$(MODULE).vvp: $(MODULE).v $(MODULE)_tb.v $(MODULE)_drivers.mem + iverilog -DICARUS_VERILOG -Iaxis_governor/ -I../marcos_macros/ -Iaxis_headerizer/ -o $(MODULE).vvp $(MODULE)_tb.v + +# Run the Verilog simulator +$(MODULE).vcd: $(MODULE).vvp + vvp $(MODULE).vvp + +clean: + rm -rf $(MODULE).vvp + rm -rf $(MODULE).vcd + rm -rf ${out_dir} + rm -rf ${ip_name}_tmp_proj + +# Packages into a Vivado IP +ip: clean + rm -rf ${out_dir} + mkdir -p ${out_dir}/src + cp dbg_guv.v axis_governor/axis_governor.v ../marcos_macros/macros.vh axis_headerizer/axis_headerizer.v ${out_dir}/src + vivado -nolog -nojournal -notrace -mode batch -source ip_maker.tcl -tclargs ${out_dir} ${ip_name} ${part_no} + rm -rf ${ip_name}_tmp_proj + rm -f *log + rm -rf .Xil + rm -f vivado* + +syntax_check: + iverilog -DICARUS_VERILOG -Iaxis_governor/ -I../marcos_macros/ -Iaxis_headerizer/ -E $(MODULE).v + rm -rf a.out + diff --git a/middleware/hls/dbg_guv/axis_governor/Makefile b/middleware/hls/dbg_guv/axis_governor/Makefile new file mode 100644 index 00000000..26693923 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_governor/Makefile @@ -0,0 +1,42 @@ +# These variables are for running the testbench +MODULE := axis_governor + +# By default, package as an ip +default: + echo "Warning: axis_governor is not meant to be used as a separate IP in Galapagos" + echo "Please see https://github.com/esophagus-now/ye_olde_verilogge" + +# This is to run the testbench +tb: $(MODULE).vcd + +# This opens the testbench in gtkwave +open: $(MODULE).vcd + gtkwave $(MODULE).vcd --autosavename & + +# Compile the Verilog into Icarus's special format +$(MODULE).vvp: $(MODULE).v $(MODULE)_tb.v $(MODULE)_drivers.mem + iverilog -DICARUS_VERILOG -o $(MODULE).vvp $(MODULE)_tb.v + +# Run the Verilog simulator +$(MODULE).vcd: $(MODULE).vvp + vvp $(MODULE).vvp + +clean: + rm -rf $(MODULE).vvp + rm -rf $(MODULE).vcd + +# Packages into a Vivado IP +ip: clean + rm -rf ${out_dir} + mkdir -p ${out_dir}/src + cp $(shell find ${src_dir} -name "*.v" -o -name "*vh" -o -name "*sv" | grep -v "tb") ${out_dir}/src + vivado -nolog -nojournal -notrace -mode batch -source ip_maker.tcl -tclargs ${out_dir} ${ip_name} ${part_no} + rm -rf ${ip_name}_tmp_proj + rm -f *log + rm -rf .Xil + rm -f vivado* + +syntax_check: + iverilog -DICARUS_VERILOG -E $(MODULE).v + rm -rf a.out + diff --git a/middleware/hls/dbg_guv/axis_governor/axis_governor.gtkw b/middleware/hls/dbg_guv/axis_governor/axis_governor.gtkw new file mode 100644 index 00000000..a6b37623 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_governor/axis_governor.gtkw @@ -0,0 +1,70 @@ +[*] +[*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI +[*] Sat Feb 8 16:49:06 2020 +[*] +[dumpfile] "/home/mahkoe/research/galapagos/middleware/hls/axis_governor/axis_governor.vcd" +[dumpfile_mtime] "Sat Feb 8 16:32:37 2020" +[dumpfile_size] 32134 +[savefile] "/home/mahkoe/research/galapagos/middleware/hls/axis_governor/axis_governor.gtkw" +[timestart] 4798000 +[size] 1916 1060 +[pos] -1 -541 +*-15.412784 4495010 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] testbench_template. +[treeopen] testbench_template.DUT. +[sst_width] 233 +[signals_width] 158 +[sst_expanded] 1 +[sst_vpaned_height] 575 +@28 +testbench_template.clk +testbench_template.pause +testbench_template.drop +testbench_template.log +@29 +testbench_template.inject +@800200 +-in +@24 +[color] 2 +testbench_template.in_TDATA[7:0] +@28 +[color] 2 +testbench_template.in_TVALID +[color] 2 +testbench_template.in_TREADY +@1000200 +-in +@800200 +-log +@24 +[color] 3 +testbench_template.log_TDATA[7:0] +@28 +testbench_template.log_TVALID +testbench_template.log_TREADY +@1000200 +-log +@800200 +-inj +@24 +[color] 2 +testbench_template.inj_TDATA[7:0] +@28 +[color] 2 +testbench_template.inj_TVALID +[color] 2 +testbench_template.inj_TREADY +@1000200 +-inj +@800200 +-out +@24 +testbench_template.out_TDATA[7:0] +@28 +testbench_template.out_TVALID +testbench_template.out_TREADY +@1000200 +-out +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/middleware/hls/dbg_guv/axis_governor/axis_governor.v b/middleware/hls/dbg_guv/axis_governor/axis_governor.v new file mode 100644 index 00000000..52abbe69 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_governor/axis_governor.v @@ -0,0 +1,152 @@ +`timescale 1ns / 1ps + +/* +This module is an AXI Stream passthrough, but with options to modify the +underlying stream. Supported operations: + + - Injecting flits + - Forcing ready to low (i.e. pausing) + - Copying flits to another interface (i.e. logging) + - Dropping flits (while potentially logging them) + +This module is designed to be used inside of an FSM which can, among other +things, accept commands to allow single-stepping, running to a "breakpoint" or +"watchpoint", repackage logged streams to move the side channels into TDATA, +etc. For now I just wanted to simulate this. + +This module requires 2 LUTs for the glue logic, and whatever LUTs/MUXes are +needed for the multiplexers at the output + +*/ + +//TODO: Find out how to selectively disable certain side channels +//Default widths correspond to Galapagos defaults +module axis_governor #( + parameter DATA_WIDTH = 64, + parameter DEST_WIDTH = 16, + parameter ID_WIDTH = 16 +) ( + input wire clk, + + //Input AXI Stream. + input wire [DATA_WIDTH-1:0] in_TDATA, + input wire in_TVALID, + output wire in_TREADY, + input wire [DATA_WIDTH/8 -1:0] in_TKEEP, + input wire [DEST_WIDTH -1:0] in_TDEST, + input wire [ID_WIDTH -1:0] in_TID, + input wire in_TLAST, + + //Inject AXI Stream. + input wire [DATA_WIDTH-1:0] inj_TDATA, + input wire inj_TVALID, + output wire inj_TREADY, + input wire [DATA_WIDTH/8 -1:0] inj_TKEEP, + input wire [DEST_WIDTH -1:0] inj_TDEST, + input wire [ID_WIDTH -1:0] inj_TID, + input wire inj_TLAST, + + //Output AXI Stream. + output wire [DATA_WIDTH-1:0] out_TDATA, + output wire out_TVALID, + input wire out_TREADY, + output wire [DATA_WIDTH/8 -1:0] out_TKEEP, + output wire [DEST_WIDTH -1:0] out_TDEST, + output wire [ID_WIDTH -1:0] out_TID, + output wire out_TLAST, + + //Log AXI Stream. + output wire [DATA_WIDTH-1:0] log_TDATA, + output wire log_TVALID, + input wire log_TREADY, + output wire [DATA_WIDTH/8 -1:0] log_TKEEP, + output wire [DEST_WIDTH -1:0] log_TDEST, + output wire [ID_WIDTH -1:0] log_TID, + output wire log_TLAST, + + //Control signals + input wire pause, + input wire drop, + input wire log_en + //input wire inject //Not needed; this is determined from inj_TVALID +); + //Perform the tricky management of valids and readies. + //This module is defined lower down in this file + axis_governor_glue_log glue( + .in_vld(in_TVALID), + .out_rdy(out_TREADY), + .log_en(log_en), + .log_rdy(log_TREADY), + .inj_vld(inj_TVALID), + .pause(pause), + .drop(drop), + + .in_rdy(in_TREADY), + .out_vld(out_TVALID), + .log_vld(log_TVALID), + .inj_rdy(inj_TREADY) + ); + + //Connect remaining AXI Stream signals + assign out_TDATA = inj_TVALID ? inj_TDATA : in_TDATA; + assign out_TKEEP = inj_TVALID ? inj_TKEEP : in_TKEEP; + assign out_TDEST = inj_TVALID ? inj_TDEST : in_TDEST; + assign out_TID = inj_TVALID ? inj_TID : in_TID; + assign out_TLAST = inj_TVALID ? inj_TLAST : in_TLAST; + + assign log_TDATA = in_TDATA; + assign log_TKEEP = in_TKEEP; + assign log_TDEST = in_TDEST; + assign log_TID = in_TID; + assign log_TLAST = in_TLAST; +endmodule + + +//This glue logic was NOT easy to come up with. In the end I did a 128-entry +//truth table. There was no other way to be sure I covered every corner case. +//At some point I may attach the truth table with this code. +module axis_governor_glue_log ( + input wire in_vld, + input wire out_rdy, + input wire log_en, + input wire log_rdy, + input wire inj_vld, + input wire pause, + input wire drop, + + output wire in_rdy, + output wire out_vld, + output wire log_vld, + output wire inj_rdy +); + + //(~inj_vld && out_rdy) means the slave is ready to receive a flit from the + //master. OR'ing this with drop means the slave (and injector) cannot + //backpressure the master if drop is high. + // + //(log_rdy || ~log_en) means that the logger is not backpressuring the + //master + // + //Finally, in_rd is forced low if we are paused + assign in_rdy = ~pause && (log_rdy || ~log_en) && (drop || (~inj_vld && out_rdy)); + + //If the injector has valid data, it takes precedence + //The other condition means that the master is trying to send something to + //the slave, and that the logger is not backpressuring the master + assign out_vld = inj_vld || (in_vld && ~drop && ~pause && (~log_en || log_rdy)); + + //Note: this next line is written this way to let Vivado synthesize the logic + //in simple LUTs. However, it is better understood as + // + //assign log_vld = log_en && in_vld && s_rdy; + // + //Also note that log_vld depends on log_rdy. Technically, this violates the + //AXI Stream specification. In order for this to work, you need to connect + //the log AXI Stream channel to a core which does not have a combinational + //path from ready to valid (a register slice, for example) + assign log_vld = log_en && ~pause && in_vld && (drop || (~inj_vld && out_rdy)); + + //At least this is simple... + assign inj_rdy = out_rdy; + +endmodule diff --git a/middleware/hls/dbg_guv/axis_governor/axis_governor_drivers.mem b/middleware/hls/dbg_guv/axis_governor/axis_governor_drivers.mem new file mode 100644 index 00000000..f4f08092 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_governor/axis_governor_drivers.mem @@ -0,0 +1 @@ +Nothing to see here... diff --git a/middleware/hls/dbg_guv/axis_governor/axis_governor_tb.v b/middleware/hls/dbg_guv/axis_governor/axis_governor_tb.v new file mode 100644 index 00000000..8aa65ea3 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_governor/axis_governor_tb.v @@ -0,0 +1,153 @@ +`timescale 1ns / 1ps + +/* +testbench_template.v + +Replace innards with desired logic +*/ + +`include "axis_governor.v" + +module testbench_template # ( + parameter DATA_WIDTH = 8, + parameter DEST_WIDTH = 16, + parameter ID_WIDTH = 16 +); + reg clk = 0; + //Input AXI Stream. + reg [DATA_WIDTH-1:0] in_TDATA = 1; + reg in_TVALID = 0; + wire in_TREADY; + reg [DATA_WIDTH/8 -1:0] in_TKEEP = 0; + reg [DEST_WIDTH -1:0] in_TDEST = 0; + reg [ID_WIDTH -1:0] in_TID = 0; + reg in_TLAST = 0; + + //Inject AXI Stream. + reg [DATA_WIDTH-1:0] inj_TDATA = 0; + reg inj_TVALID = 0; + wire inj_TREADY; + reg [DATA_WIDTH/8 -1:0] inj_TKEEP = 0; + reg [DEST_WIDTH -1:0] inj_TDEST = 0; + reg [ID_WIDTH -1:0] inj_TID = 0; + reg inj_TLAST = 0; + + //Output AXI Stream. + wire [DATA_WIDTH-1:0] out_TDATA; + wire out_TVALID; + reg out_TREADY = 0; + wire [DATA_WIDTH/8 -1:0] out_TKEEP; + wire [DEST_WIDTH -1:0] out_TDEST; + wire [ID_WIDTH -1:0] out_TID; + wire out_TLAST; + + //Log AXI Stream. + wire [DATA_WIDTH-1:0] log_TDATA; + wire log_TVALID; + reg log_TREADY = 0; + wire [DATA_WIDTH/8 -1:0] log_TKEEP; + wire [DEST_WIDTH -1:0] log_TDEST; + wire [ID_WIDTH -1:0] log_TID; + wire log_TLAST; + + //Control signals + wire pause; + wire drop; + wire log; + + //Simulation variables + wire inject; + reg [3:0] state = 0; + + assign pause = state[0]; + assign drop = state[1]; + assign log = state[2]; + assign inject = state[3]; + + integer fd, dummy; + + initial begin + $dumpfile("axis_governor.vcd"); + $dumpvars; + $dumplimit(512000); + + clk <= 0; + + repeat(16) begin + repeat(32) @(negedge clk); + state <= state + 1; + end + + #10 + $finish; + end + + always #5 clk <= ~clk; + + always @(posedge clk) begin + #0.01 //Not sure why Vivado doesn't need this... + in_TVALID <= $random; + inj_TVALID <= $random & inject; + out_TREADY <= $random; + log_TREADY <= $random; + + if (in_TREADY && in_TVALID) begin + in_TDATA <= in_TDATA + 2; + end + + if (inj_TREADY && inj_TVALID) begin + inj_TDATA <= inj_TDATA + 2; + end + end + +axis_governor #( + .DATA_WIDTH(DATA_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .ID_WIDTH(ID_WIDTH) +) DUT ( + .clk(clk), + + //Input AXI Stream. + .in_TDATA(in_TDATA), + .in_TVALID(in_TVALID), + .in_TREADY(in_TREADY), + .in_TKEEP(in_TKEEP), + .in_TDEST(in_TDEST), + .in_TID(in_TID), + .in_TLAST(in_TLAST), + + //Inject AXI Stream. + .inj_TDATA(inj_TDATA), + .inj_TVALID(inj_TVALID), + .inj_TREADY(inj_TREADY), + .inj_TKEEP(inj_TKEEP), + .inj_TDEST(inj_TDEST), + .inj_TID(inj_TID), + .inj_TLAST(inj_TLAST), + + //Output AXI Stream. + .out_TDATA(out_TDATA), + .out_TVALID(out_TVALID), + .out_TREADY(out_TREADY), + .out_TKEEP(out_TKEEP), + .out_TDEST(out_TDEST), + .out_TID(out_TID), + .out_TLAST(out_TLAST), + + //Log AXI Stream. + .log_TDATA(log_TDATA), + .log_TVALID(log_TVALID), + .log_TREADY(log_TREADY), + .log_TKEEP(log_TKEEP), + .log_TDEST(log_TDEST), + .log_TID(log_TID), + .log_TLAST(log_TLAST), + + //Control signals + .pause(pause), + .drop(drop), + .log_en(log) +); + + +endmodule diff --git a/middleware/hls/dbg_guv/axis_governor/design_docs/arbitration.txt b/middleware/hls/dbg_guv/axis_governor/design_docs/arbitration.txt new file mode 100644 index 00000000..b79a58a7 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_governor/design_docs/arbitration.txt @@ -0,0 +1,376 @@ +=================================================== +HOW DOES THE ARBITRATION WORK IN THE AXIS GOVERNOR? +=================================================== + +Suppose I have an AXI Stream between two points: + + +-----------+ +-----------+ + | vld|------------------------------------>|vld | + | | | | + | Alex | | Brittany | + | | | | + | rdy|<------------------------------------|rdy | + +-----------+ +-----------+ + + +(Note: "Alice and Bob" have been used so often that I have named my two parties +differently) + +We will consider how we can add some logic to allow: + - Pausing transmission + - Forcing flits to be dropped + - Injecting flits + - Logging flits + +Our technique will be to do each of features separately, then to combine them. +After that, I will present the technique I used to verify that we (probably) +didn't miss any corner cases. + +======= +PAUSING +======= + +We will add an input signal which stops transmission if it is high, and has no +effect if it is low: + + pause + + + | __ + +-----------+ +-o| \ +-----------+ + | vld|----------------|--|__/------------->|vld | + | | | | | + | Alex | __ | | Brittany | + | | / |o-+ | | + | rdy|<---------\__|-----------------------|rdy | + +-----------+ +-----------+ + +(These are AND gates) + +If pause is low, Brittany.vld = Alex.vld and Alex.rdy = Brittany.rdy. However, +if pause is high, Alex.rdy is low and Brittany.vld is low, meaning that neither +will ever see a flit go by. + + +======== +DROPPING +======== + +Now we will add an input which causes Alex to think Brittany is always ready, +but Brittany thinks Alex is never valid (in other words, flits from Alex will +be dropped): + + + drop + + + | __ + +-----------+ +-o| \ +-----------+ + | vld|----------------|--|__/------------->|vld | + | | | | | + | Alex | ___ | | Brittany | + | | / /-+ | | + | rdy|<----------\__\----------------------|rdy | + +-----------+ +-----------+ + +(The top gate is an AND and the bottom is an OR) + + +========= +INJECTING +========= + +This is a little more complicated. We'll add an injection input (as an AXI +Stream master). If there is an injected flit we need to backpressure Alex, but +we still want transmissions to continue otherwise: + + ___ + +-----------+ +-\ \ +-----------+ + | vld|---------------|-/__/--------------->|vld | + | | | | | + | Alex | __ | | Brittany | + | | / |o-+ | | + | rdy|<--------\__|--|------+--------------|rdy | + +-----------+ | | +-----------+ + | | + | v + +------------+ + | vld rdy | + | | + | Inject | + | | + +------------+ + +Suppose Inject.vld is low; then (Brittany.vld = Alex.vld) and (Alex.rdy = +Brittany.rdy). This means that transmissions can continue if we're not +injecting anything. + +Suppose Inject.vld is high; then Alex.rdy is low (thus causing backpressure) +and Brittany.vld is high (which is correct, since Inject.vld is high and is the +source of flits for Brittany). + + +======= +LOGGING +======= + +NAÏVE METHOD +------------ + +This is the most complicated task. We will add a log output (as an AXI Stream +slave). Alex must be backpressured if either Brittany or the logging output is +not ready: + + +-----------+ +-----------+ + | vld|---------------+-------------------->|vld | + | | | | | + | Alex | | __ | Brittany | + | | | / |-+ | | + | rdy|<----------------\__|-|--------------|rdy | + +-----------+ | | +-----------+ + | | + v | + +------------+ + | vld rdy | + | | + | Log | + | | + +------------+ + +However, we run into trouble if we do this: if Alex is valid, then the logger +or Brittany will record a flit whenever they are ready. However, they must both +be ready before Alex will consider a flit to have been read. + +In other words, flits are copied if exactly one of Brittany or the logging +output is ready! + + +ELIMINATING INCORRECT FLIT COPIES +--------------------------------- + +To fix this, we should make sure that Brittany.vld and Log.vld are only high +when a flit is read from master: + + + __ + +-----------+ +-| \ +-----------+ + | vld|---|-|__/------+-------------------->|vld | + | | | | | | + | Alex | | | __ | Brittany | + | | | | / |-+ | | + | rdy|<--+-------------\__|-|--------------|rdy | + +-----------+ | | +-----------+ + | | + v | + +------------+ + | vld rdy | + | | + | Log | + | | + +------------+ + +This does techninically solve the problem, but we have now violated the AXI +Stream specification! In the spec, it is legal for a slave to have a +combinational path from its "valid" input to its "ready" output. By letting +Brittany.vld = (Alex.vld && Alex.rdy) = (Alex.vld && Brittany.rdy && Log.rdy) +we might make a combinational loop involving Brittany.vld and Brittany.rdy! + +And by the way, this same problem also exists for the Log output. + + +FIXING COMBINATIONAL LOOPS +-------------------------- + +The idea is this: Log is only valid if Alex is valid and Brittany is ready. +Likewise, Brittany is only valid if Alex is valid and the Log is ready. It's +actually easier to understand this reasoning if we had, say, five slaves. The +master should never send unless all five are ready, but each slave should never +receive unless the master is valid AND all the other slaves are ready. + +The final circuit is as follows: + __ + +-----------+ +-| \ +-----------+ + | vld|--------------+-------|-|__/-------->|vld | + | | | /--+ | | | + | Alex | --- | | | Brittany | + | | __ \_/ | | | | + | rdy|<------/ |----|---+--|--------------|rdy | + +-----------+ \__|----|------+ +-----------+ + | | + v | + +------------+ +------------------+ + | vld rdy | | a b | + | | | | | | + | Log | | --- | + | | | \_/ f = a AND b| + +------------+ | | | + | f | + +------------------+ + +This is much more subtle. If Log.rdy is low, then Alex.rdy is low and +Brittany.vld is low; this backpressures Alex and makes sure Brittany doesn't +read anything. + +If Log.rdy is high, then Alex.rdy = Brittany.rdy and Brittany.vld = Alex.vld, +which guarantees correct transmissions from Alex to Brittany. Furthermore, +Log.vld = (Alex.vld && Alex.rdy), which ensures that the logger only logs flits +that Alex transmitted (this is similar to a snooping interface). + +If you're like me, at this point you're thinking "well, I guess that makes +sense, but how do I know I didn't miss anything?". As far as I know, the only +way to be sure is to do a truth table and inspect every single circumstance +manually. See the section at the end of this document. + + +=================== +PUTTING IT TOGETHER +=================== + +This section presents how I chose to combine all these techniques. There are +actually several options, which could all be considered correct, though they +have different "precedence"; for example, if "drop" has higher precedence than +"pause", it means that flits from Alex will be dropped if both these signals +are asserted. On the other hand, if "pause" has higher precence, Alex will be +forcibly backpressured when both signals are asserted. + +Here is a high-level view of the AXI Stream governor: + + pause log drop + | | | + v v v + +-----------+ +---+ +-------------+ +---+ +-------------+ +-----------+ + | vld|-| P |-| |-| D |-| |-|vld | + | | | A | | Logging | | R | | Injection | | | + | Alex | | U | | arbitration | | O | | arbitration | | Brittany | + | | | S | | logic | | P | | logic | | | + | rdy|-| E |-| |-| |-| |-|rdy | + +-----------+ +---+ +-------------+ +---+ +-------------+ +-----------+ + | | | | + | | | | + +------------+ +------------+ + | vld rdy | | vld rdy | + | | | | + | Log | | Inject | + | | | | + +------------+ +------------+ + +The trick is to notice that each individual technique still looks like an AXI +Stream after it has been applied, so we can literally chain them together. My +choice was to let pause have highest precedence, and that injections should +still be honoured even when drop is high. Furthermore, logs can still occur +even if drop is high (and in fact, if drop is high, logs are not stopped even +if Brittany.rdy is low). + +The final circuit diagram is obtained by substituting in the individual +circuits. For completeness, I have drawn it up; please see governor.png in this +folder. It was generated by the excellent Logisim program using governor.circ. + +Finally, I went in by hand and wrote down the logic expressions for each +output, and I also simplified them slightly. Here are my results (copied +straight out of my Verilog implementation): + + //(~inj_vld && out_rdy) means the slave is ready to receive a flit from the + //master. OR'ing this with drop means the slave (and injector) cannot + //backpressure the master if drop is high. + // + //(log_rdy || ~log_en) means that the logger is not backpressuring the + //master + // + //Finally, in_rd is forced low if we are paused + assign in_rdy = ~pause && (log_rdy || ~log_en) && (drop || (~inj_vld && out_rdy)); + + //If the injector has valid data, it takes precedence + //The other condition means that the master is trying to send something to + //the slave, and that the logger is not backpressuring the master + assign out_vld = inj_vld || (in_vld && ~drop && ~pause && (~log_en || log_rdy)); + + //Note: this next line is written this way to let Vivado synthesize the logic + //in simple LUTs. However, it is better understood as + // + //assign log_vld = log_en && in_vld && m_rdy; + // + //Also note that log_vld depends on log_rdy. Technically, this violates the + //AXI Stream specification. In order for this to work, you need to connect + //the log AXI Stream channel to a core which does not have a combinational + //path from ready to valid (a register slice, for example) + assign log_vld = log_en && ~pause && in_vld && (drop || (~inj_vld && out_rdy)); + + //At least this is simple... + assign inj_rdy = out_rdy; + +The next section talks about how I verified this circuit (informally) and that +my logic expressions correctly matched my diagram. + +=========================== +HOW DO WE KNOW IF IT WORKS? +=========================== + +Once I drew up the circuit in Logisim, I asked Logisim to produce a truth table +for the circuit (Project -> Analyze Circuit). I then pasted this table directly +into a spreadsheet. + +Then, I came up with a list of errors. If any of these errors occur on any line +of the truth table, the circuit would have to be reviewed: + +-> badeat: Alex thinks a flit was consumed, but the governor was paused + +-> missdrop: Governor was dropping, but Brittany received a flit (and nothing + was injected) + +-> wrongdrop: Alex thinks a flit was consumed, but Brittany did not receive + anything, and the governor was not dropping + +-> badlog: The Logger read something, but (logging was off OR Alex did not send + anything) + +-> logcopy (special case of badlog): The Logger read something, but Alex did + not send anything. + +-> slvcopy: Brittany read something, but neither Alex nor the injector sent + anything + +-> misslog: Alex sent something, logging was on, but the logger did not read + anything + +-> missout: Brittany did not read anything, but ((Alex sent something AND we + weren't paused AND we're not dropping) OR (the injector sent something)) + +-> missinject (special case of missout): the injector send something but + Brittany didn't read anything + +-> injectclobber: Alex sent something, we're not paused, we're not dropping, + AND the injector sent something. + +-> badpause: Alex sent something, but we're paused + +-> wronginject: This turned out to be identical to missinject. I've just left + the description in here to better explain the attached spreadsheets + +I have no formal proof that these are all the errors that could happen, but I +can't think of any more, so that's good enough for me. + +By the way, the size of the truth table outputs is 128 by 4, or 512 cells. In +theory, for me to formally prove correctness, I would have to come up with a +formal specification for each of the output cells (either 1, 0, or Don't Care). +I decided not to do that. Who knows, maybe the above method is a more efficient +way of doing just that? + +EXPLANATION OF THE ATTACHED FILES AND SPREADSHEETS +-------------------------------------------------- + +First of all, notice that there is a gov.circ, gov2.circ, and gov3.circ in +early_attempts/. These were the attempts I did by just trying to draw the +circuit without really thinking about it in a structured way. In other words, +this whole discussion about combining the pause, drop, log, and inject +primitives (taking care of precedence) didn't occur to me until I had tried to +make the circuit twice and found errors. + +The first three sheets in the spreadsheet show the truth tables for my first +three attempts. The fourth one shows the final circuit that passed all my tests. + +The fifth spreadsheet was how I confirmed to myself that I had correctly +transcribed the logical formulas from the diagram. The truth table is the same +one from the correct circuit. + +In all the spreadsheets, immediately to the right of the (nicely coloured) +truth table, are little helper signals for each of the four AXI Streams. They +are simply the AND of valid and ready, indicating (perceived) flit +transmission. diff --git a/middleware/hls/dbg_guv/axis_governor/design_docs/early_attempts/gov.circ b/middleware/hls/dbg_guv/axis_governor/design_docs/early_attempts/gov.circ new file mode 100644 index 00000000..5ed9edb7 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_governor/design_docs/early_attempts/gov.circ @@ -0,0 +1,222 @@ + + +This file is intended to be loaded by Logisim (http://www.cburch.com/logisim/). + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/middleware/hls/dbg_guv/axis_governor/design_docs/early_attempts/gov2.circ b/middleware/hls/dbg_guv/axis_governor/design_docs/early_attempts/gov2.circ new file mode 100644 index 00000000..677f386b --- /dev/null +++ b/middleware/hls/dbg_guv/axis_governor/design_docs/early_attempts/gov2.circ @@ -0,0 +1,219 @@ + + +This file is intended to be loaded by Logisim (http://www.cburch.com/logisim/). + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/middleware/hls/dbg_guv/axis_governor/design_docs/early_attempts/gov3.circ b/middleware/hls/dbg_guv/axis_governor/design_docs/early_attempts/gov3.circ new file mode 100644 index 00000000..58f43443 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_governor/design_docs/early_attempts/gov3.circ @@ -0,0 +1,263 @@ + + +This file is intended to be loaded by Logisim (http://www.cburch.com/logisim/). + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/middleware/hls/dbg_guv/axis_governor/design_docs/gov4.circ b/middleware/hls/dbg_guv/axis_governor/design_docs/gov4.circ new file mode 100644 index 00000000..0646a0b8 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_governor/design_docs/gov4.circ @@ -0,0 +1,261 @@ + + +This file is intended to be loaded by Logisim (http://www.cburch.com/logisim/). + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/middleware/hls/dbg_guv/axis_governor/design_docs/governor.png b/middleware/hls/dbg_guv/axis_governor/design_docs/governor.png new file mode 100644 index 00000000..4b513a6c Binary files /dev/null and b/middleware/hls/dbg_guv/axis_governor/design_docs/governor.png differ diff --git a/middleware/hls/dbg_guv/axis_governor/design_docs/governor.xlsx b/middleware/hls/dbg_guv/axis_governor/design_docs/governor.xlsx new file mode 100644 index 00000000..89ffde9b Binary files /dev/null and b/middleware/hls/dbg_guv/axis_governor/design_docs/governor.xlsx differ diff --git a/middleware/hls/dbg_guv/axis_headerizer/Makefile b/middleware/hls/dbg_guv/axis_headerizer/Makefile new file mode 100644 index 00000000..164a22b9 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_headerizer/Makefile @@ -0,0 +1,31 @@ +# These variables are for running the testbench +MODULE := axis_headerizer + +# By default, try not to mess up Galapagos +default: + echo "Warning: axis_headerizer is not meant to be used as a separate IP in Galapagos" + echo "Please see https://github.com/esophagus-now/ye_olde_verilogge" + +# This is to run the testbench +tb: $(MODULE).vcd + +# This opens the testbench in gtkwave +open: $(MODULE).vcd + gtkwave $(MODULE).vcd --autosavename & + +# Compile the Verilog into Icarus's special format +$(MODULE).vvp: $(MODULE).v $(MODULE)_tb.v + iverilog -DICARUS_VERILOG -I../../marcos_macros/ -o $(MODULE).vvp $(MODULE)_tb.v + +# Run the Verilog simulator +$(MODULE).vcd: $(MODULE).vvp + vvp $(MODULE).vvp + +clean: + rm -rf $(MODULE).vvp + rm -rf $(MODULE).vcd + +syntax_check: + iverilog -DICARUS_VERILOG -I../../marcos_macros/ -E $(MODULE).v + rm -rf a.out + diff --git a/middleware/hls/dbg_guv/axis_headerizer/axis_headerizer.gtkw b/middleware/hls/dbg_guv/axis_headerizer/axis_headerizer.gtkw new file mode 100644 index 00000000..23b50171 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_headerizer/axis_headerizer.gtkw @@ -0,0 +1,52 @@ +[*] +[*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI +[*] Sat Feb 22 21:16:16 2020 +[*] +[dumpfile] "/home/mahkoe/research/ye_olde_verilogge/axis_headerizer/axis_headerizer.vcd" +[dumpfile_mtime] "Sat Feb 22 21:15:30 2020" +[dumpfile_size] 45706 +[savefile] "/home/mahkoe/research/ye_olde_verilogge/axis_headerizer/axis_headerizer.gtkw" +[timestart] 0 +[size] 1916 1060 +[pos] -1 -1 +*-14.040150 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] axis_headerizer_tb. +[sst_width] 214 +[signals_width] 166 +[sst_expanded] 1 +[sst_vpaned_height] 314 +@28 +axis_headerizer_tb.clk +@800200 +-sides +@22 +[color] 2 +axis_headerizer_tb.sides_TDATA[63:0] +[color] 2 +axis_headerizer_tb.sides_TVALID +[color] 2 +axis_headerizer_tb.sides_TREADY +[color] 2 +axis_headerizer_tb.sides_TKEEP[7:0] +[color] 2 +axis_headerizer_tb.sides_TLAST +[color] 2 +axis_headerizer_tb.sides_TDEST[15:0] +[color] 2 +axis_headerizer_tb.sides_TID[15:0] +[color] 2 +axis_headerizer_tb.sides_TUSER[7:0] +@1000200 +-sides +@800200 +-hdr +@22 +axis_headerizer_tb.hdr_TDATA[63:0] +axis_headerizer_tb.hdr_TVALID +axis_headerizer_tb.hdr_TREADY +axis_headerizer_tb.hdr_TKEEP[7:0] +axis_headerizer_tb.hdr_TLAST +@1000200 +-hdr +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/middleware/hls/dbg_guv/axis_headerizer/axis_headerizer.v b/middleware/hls/dbg_guv/axis_headerizer/axis_headerizer.v new file mode 100644 index 00000000..cf04e024 --- /dev/null +++ b/middleware/hls/dbg_guv/axis_headerizer/axis_headerizer.v @@ -0,0 +1,70 @@ +`timescale 1ns / 1ps + + +`include "macros.vh" + +module axis_headerizer # ( + parameter DATA_WIDTH = 64, + parameter DEST_WIDTH = 16, + parameter ID_WIDTH = 16, + parameter USER_WIDTH = 8, + parameter RESET_TYPE = `NO_RESET, + parameter ENABLE_TLAST_HACK = 0 +) ( + input wire clk, + input wire rst, + + `in_axis_kl(sides, DATA_WIDTH), + input wire [DEST_WIDTH -1:0] sides_TDEST, + input wire [ID_WIDTH -1:0] sides_TID, + input wire [USER_WIDTH -1:0] sides_TUSER, + + `out_axis_kl(hdr, DATA_WIDTH) +); + + `localparam WAIT_FIRST_VLD = 0; + `localparam WAIT_LAST = 1; + + reg state = WAIT_FIRST_VLD; + + `wire_rst_sig; + +`genif (RESET_TYPE == `NO_RESET) begin + always @(posedge clk) begin + case (state) + WAIT_FIRST_VLD: + state <= `axis_flit(hdr) ? WAIT_LAST : WAIT_FIRST_VLD; + WAIT_LAST: + state <= `axis_last(hdr) ? WAIT_FIRST_VLD : WAIT_LAST; + endcase + end +`else_gen + always @(posedge clk) begin + if (rst_sig) begin + state <= WAIT_FIRST_VLD; + end else begin + case (state) + WAIT_FIRST_VLD: + state <= `axis_flit(hdr) ? WAIT_LAST : WAIT_FIRST_VLD; + WAIT_LAST: + state <= `axis_last(hdr) ? WAIT_FIRST_VLD : WAIT_LAST; + endcase + end + end +`endgen + + //TODO: Parameterize which sidechannels are present? + `localparam PAD_WIDTH = DATA_WIDTH - (1 + DEST_WIDTH + ID_WIDTH + USER_WIDTH); + wire [DATA_WIDTH -1:0] header = {{PAD_WIDTH{1'b0}}, sides_TLAST, sides_TDEST, sides_TID, sides_TUSER}; + + assign hdr_TDATA = (state == WAIT_LAST) ? sides_TDATA : header; + assign hdr_TVALID = sides_TVALID; + assign sides_TREADY = (state == WAIT_LAST) && hdr_TREADY; + assign hdr_TKEEP = (state == WAIT_LAST) ? sides_TKEEP : {(DATA_WIDTH/8){1'b1}}; +`genif (ENABLE_TLAST_HACK == 0) begin + assign hdr_TLAST = (state == WAIT_LAST) && sides_TLAST; +`else_gen + assign hdr_TLAST = (state == WAIT_LAST); +`endgen + +endmodule diff --git a/middleware/hls/dbg_guv/axis_headerizer/axis_headerizer_tb.v b/middleware/hls/dbg_guv/axis_headerizer/axis_headerizer_tb.v new file mode 100644 index 00000000..5097b33c --- /dev/null +++ b/middleware/hls/dbg_guv/axis_headerizer/axis_headerizer_tb.v @@ -0,0 +1,74 @@ +`timescale 1ns / 1ps + +/* +testbench_template.v + +Replace innards with desired logic +*/ + +`include "macros.vh" +`include "axis_headerizer.v" + +module axis_headerizer_tb # ( + parameter DATA_WIDTH = 64, + parameter DEST_WIDTH = 16, + parameter ID_WIDTH = 16, + parameter USER_WIDTH = 8, + parameter RESET_TYPE = `NO_RESET +); + reg clk = 0; + reg rst = 0; + + `sim_in_axis_kl(sides, DATA_WIDTH); + reg [DEST_WIDTH -1:0] sides_TDEST = 0; + reg [ID_WIDTH -1:0] sides_TID = 0; + reg [USER_WIDTH -1:0] sides_TUSER = 0; + + `sim_out_axis_kl(hdr, DATA_WIDTH); + + + initial begin + $dumpfile("axis_headerizer.vcd"); + $dumpvars; + $dumplimit(512000); + + sides_TVALID <= 1; + + # 2000 + $finish; + + + end + + always #5 clk <= ~clk; + + always @(posedge clk) begin + if (`axis_flit(sides)) begin + sides_TDATA <= sides_TDATA + 3; //Could have done +1, but +3 more interesting + sides_TKEEP <= $random; + sides_TLAST <= $random; + sides_TDEST <= $random; + sides_TID <= $random; + sides_TUSER <= $random; + end + end + + axis_headerizer # ( + .DATA_WIDTH(DATA_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .ID_WIDTH(ID_WIDTH), + .USER_WIDTH(USER_WIDTH), + .RESET_TYPE(RESET_TYPE) + ) DUT ( + .clk(clk), + .rst(rst), + + `inst_axis_kl(sides, sides), + .sides_TDEST(sides_TDEST), + .sides_TID(sides_TID), + .sides_TUSER(sides_TUSER), + + `inst_axis_kl(hdr, hdr) + ); + +endmodule diff --git a/middleware/hls/dbg_guv/dbg_guv.gtkw b/middleware/hls/dbg_guv/dbg_guv.gtkw new file mode 100644 index 00000000..0ca29994 --- /dev/null +++ b/middleware/hls/dbg_guv/dbg_guv.gtkw @@ -0,0 +1,144 @@ +[*] +[*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI +[*] Sat Feb 29 04:00:26 2020 +[*] +[dumpfile] "/home/mahkoe/research/ye_olde_verilogge/axis_governor/better_axis_intf/dbg_guv.vcd" +[dumpfile_mtime] "Sat Feb 29 04:00:19 2020" +[dumpfile_size] 79345 +[savefile] "/home/mahkoe/research/ye_olde_verilogge/axis_governor/better_axis_intf/dbg_guv.gtkw" +[timestart] 250300 +[size] 1916 1060 +[pos] -1 -1 +*-15.640755 549100 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] dbg_guv_tb. +[treeopen] dbg_guv_tb.ctl1. +[treeopen] dbg_guv_tb.ctl2. +[treeopen] dbg_guv_tb.ctl2.ello_guvna. +[sst_width] 279 +[signals_width] 237 +[sst_expanded] 1 +[sst_vpaned_height] 568 +@28 +dbg_guv_tb.clk +dbg_guv_tb.rst +@22 +dbg_guv_tb.cmd_in_TDATA[63:0] +@28 +dbg_guv_tb.cmd_out_TVALID +@800200 +-in1 +@24 +[color] 2 +dbg_guv_tb.in1_TDATA[63:0] +@28 +[color] 2 +dbg_guv_tb.in1_TVALID +[color] 2 +dbg_guv_tb.in1_TREADY +@22 +[color] 2 +dbg_guv_tb.in1_TKEEP[7:0] +@28 +[color] 2 +dbg_guv_tb.in1_TLAST +@22 +[color] 2 +dbg_guv_tb.in1_TDEST[15:0] +[color] 2 +dbg_guv_tb.in1_TID[15:0] +@1000200 +-in1 +@800200 +-out1 +@24 +dbg_guv_tb.out1_TDATA[63:0] +@28 +dbg_guv_tb.out1_TVALID +dbg_guv_tb.out1_TREADY +@22 +dbg_guv_tb.out1_TKEEP[7:0] +@28 +dbg_guv_tb.out1_TLAST +@22 +dbg_guv_tb.out1_TDEST[15:0] +dbg_guv_tb.out1_TID[15:0] +@1000200 +-out1 +@800200 +-log1 +@22 +[color] 3 +dbg_guv_tb.log_catted1_TDATA[71:0] +@28 +[color] 3 +dbg_guv_tb.log_catted1_TVALID +[color] 3 +dbg_guv_tb.log_catted1_TREADY +[color] 3 +dbg_guv_tb.log_catted1_TLAST +@1000200 +-log1 +@800200 +-dbg_guv1 +@28 +[color] 7 +dbg_guv_tb.ctl1.pause +[color] 7 +dbg_guv_tb.ctl1.drop +[color] 7 +dbg_guv_tb.ctl1.keep_dropping +[color] 7 +dbg_guv_tb.ctl1.log_en +[color] 7 +dbg_guv_tb.ctl1.keep_logging +@1000200 +-dbg_guv1 +@29 +dbg_guv_tb.ctl2.latch_sig +@28 +dbg_guv_tb.ctl2.ello_guvna.inj_TVALID +@800200 +-in2 +@24 +[color] 2 +dbg_guv_tb.in2_TDATA[63:0] +@28 +[color] 2 +dbg_guv_tb.in2_TVALID +[color] 2 +dbg_guv_tb.in2_TREADY +[color] 2 +dbg_guv_tb.in2_TLAST +@22 +[color] 2 +dbg_guv_tb.in2_TKEEP[7:0] +[color] 2 +dbg_guv_tb.in2_TDEST[15:0] +[color] 2 +dbg_guv_tb.in2_TID[15:0] +@1000200 +-in2 +@800200 +-out2 +@22 +dbg_guv_tb.out2_TDATA[63:0] +@28 +dbg_guv_tb.out2_TVALID +dbg_guv_tb.out2_TREADY +@22 +dbg_guv_tb.out2_TKEEP[7:0] +@28 +dbg_guv_tb.out2_TLAST +@22 +dbg_guv_tb.out2_TDEST[15:0] +dbg_guv_tb.out2_TID[15:0] +@1000200 +-out2 +@22 +dbg_guv_tb.log_catted2_TDATA[71:0] +@28 +dbg_guv_tb.log_catted2_TVALID +dbg_guv_tb.log_catted2_TREADY +dbg_guv_tb.log_catted2_TLAST +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/middleware/hls/dbg_guv/dbg_guv.v b/middleware/hls/dbg_guv/dbg_guv.v new file mode 100644 index 00000000..0521b011 --- /dev/null +++ b/middleware/hls/dbg_guv/dbg_guv.v @@ -0,0 +1,547 @@ +`timescale 1ns / 1ps + +//TODO: Figure out how to disable the AXI Stream sidechannels using parameters +//Sadly, there is no clean way to do this..... + +/* + +This wraps around axis_governor.v, and is what will get put into your block +diagram when you use the automatic TCL scripts provided along with these cores. +Please forgive the messy organization; it's unclear how to organize things +until they're all finished. + +This controller works by maintaining two sets of registers. The first set of +registers, called S1, include: + + - drop_cnt: The number of flits to drop from the input stream + - log_cnt: The number of flits to log from the input stream + - inj_TDATA: The TDATA field of the injection input + - inj_TVALID: The TVALID field of the injection input. + - inj_T*: The other AXI Stream fields for the injection input + - keep_pausing: When 1, pauses input stream indefinitely + - keep_logging: When 1, logs input stream indefinitely + - keep_dropping: When 1, drops input stream indefinitely + +Whenever a flit is dropped(logged), drop_cnt(log_cnt) is decremented. Once the +count reaches zero, no more flits will be dropped(logged), unless +keep_dropping(keep_logging) is asserted. If the drop_cnt(log_cnt) is nonzero, +the keep_pausing signal is ignored. Otherwise, keep_pausing causes +keep_dropping(keep_logging) to be ignored. Also, as soon as the injected flit +is sent out, the inj_TVALID register is reset to zero. + +The second set of registers, called S2, is identical to the first set. In the +code, they have an "_r" at the end of their name. These registers do not +control anything, and can only be set by the command interface. The trick is +that we perform S1 = S2 when the user sends a special command. This allows the +user to build up a (possibly complex) set of register updates that will then be +applied simultaneously. + +The command interface is definitely clunky, but I've already found myself +spending a lot of time writing this code and I want to move on. It only needs +to be good enough. Anyway, it works in two steps: first you send an address, +and then you send data on the immediately following flit. The address is +formatted as {padding, dbg_core, reg}. The dbg_core address specifies which +debug core to use, and reg specifies which of the S1 registers to update within +that debug core. If reg is all ones, then S1 is copied into S2. These addresses +are aligned to the right of cmd_in_TDATA by padding on the left. The data is +formatted as {padding, data}. Everything is in big-endian. + +There's an interesting parameter I added: STICKY_MODE. When S1 is copied into +S2, I was resetting all the S1 registers back to zero. However, in some cases +this was very inconvenient, and can cost more logic if you're not using a reset +signal. So when STICKY_MODE is enabled, the S1 registers retain their values +until the user explicitly chagnes them + +*/ + + `ifdef ICARUS_VERILOG +`include "axis_governor.v" +`include "axis_headerizer.v" +`endif + +`include "macros.vh" + +module dbg_guv # ( + parameter DATA_WIDTH = 64, + parameter DEST_WIDTH = 16, + parameter ID_WIDTH = 16, + parameter CNT_SIZE = 16, + parameter ADDR_WIDTH = 10, //This gives 1024 simultaneous debug cores + parameter [ADDR_WIDTH -1:0] ADDR = 0, //Set this to be different for each + parameter RESET_TYPE = `NO_RESET, + parameter STICKY_MODE = 1, //If 1, latching registers does not reset them + parameter PIPE_STAGE = 1 //This causes a delay on cmd_out in case fanout is + //an issue +) ( + input wire clk, + input wire rst, + + //Input command stream + //This may be a bad decision, but I decided the command width should match + //the inject data width. + //How does this work if you have a bunch of streams of different sizes that + //you want to debug? Also, what about the rr_tree in that case? + //Also, this core cannot assert backpressure + input wire [DATA_WIDTH -1:0] cmd_in_TDATA, + input wire cmd_in_TVALID, + + //All the controllers are daisy-chained. If in incoming command is not for + //this controller, send it to the next one + output wire [DATA_WIDTH -1:0] cmd_out_TDATA, + output wire cmd_out_TVALID, + + //Also,since this module is intended to wrap around axis_governor, we need + //to provide access to its ports through this one. + + //Input AXI Stream. + input wire [DATA_WIDTH-1:0] in_TDATA, + input wire in_TVALID, + output wire in_TREADY, + input wire [DATA_WIDTH/8 -1:0] in_TKEEP, + input wire [DEST_WIDTH -1:0] in_TDEST, + input wire [ID_WIDTH -1:0] in_TID, + input wire in_TLAST, + + //Output AXI Stream. + output wire [DATA_WIDTH-1:0] out_TDATA, + output wire out_TVALID, + input wire out_TREADY, + output wire [DATA_WIDTH/8 -1:0] out_TKEEP, + output wire [DEST_WIDTH -1:0] out_TDEST, + output wire [ID_WIDTH -1:0] out_TID, + output wire out_TLAST, + + //Log AXI Stream. + //This core takes care of adding the TDEST, TID, TLAST, and governor ID as + //a header on logged flits. The TKEEP sidechannel is concatted for + //compatibility with the rr4 module + `out_axis_l(log_catted, DATA_WIDTH + DATA_WIDTH/8) +); + //////////////////// + //LOCAL PARAMETERS// + //////////////////// + + `localparam REG_ADDR_WIDTH = 4; + //These just clean up the code slightly + `localparam NO_RST = (RESET_TYPE == `NO_RESET); + `localparam HAS_RST = (RESET_TYPE != `NO_RESET); + + +`ifdef ICARUS_VERILOG + initial begin + $display("dbg_guv:"); + $display("--------"); + $display("DATA_WIDTH = %d", DATA_WIDTH ); + $display("DEST_WIDTH = %d", DEST_WIDTH ); + $display("ID_WIDTH = %d", ID_WIDTH ); + $display("CNT_SIZE = %d", CNT_SIZE ); + $display("ADDR_WIDTH = %d", ADDR_WIDTH ); + $display("ADDR = %d", ADDR ); + $display("RESET_TYPE = %d", RESET_TYPE ); + $display("STICKY_MODE = %d",STICKY_MODE); + $display("PIPE_STAGE = %d", PIPE_STAGE ); + end +`endif + + //////////////////////// + //FORWARD DECLARATIONS// + //////////////////////// + + //Wires needed for axis_governor connections + wire inj_TREADY; + `wire_axis_kl(log, DATA_WIDTH); + wire [DEST_WIDTH -1:0] log_TDEST; + wire [ID_WIDTH -1:0] log_TID; + + //////////////// + //HELPER WIRES// + //////////////// + + //Named subfields of command + wire [ADDR_WIDTH -1:0] cmd_core_addr = cmd_in_TDATA[ADDR_WIDTH + REG_ADDR_WIDTH -1 -: ADDR_WIDTH]; + wire [REG_ADDR_WIDTH -1:0] cmd_reg_addr = cmd_in_TDATA[REG_ADDR_WIDTH -1:0]; + //We need to know if this message was meant for us + wire msg_for_us = (cmd_core_addr == ADDR); + + wire rst_sig; +`genif (RESET_TYPE == `ACTIVE_HIGH) begin + assign rst_sig = rst; +`else_gen + assign rst_sig = ~rst; +`endgen + + /////////////////////////// + //COMMAND INTERPRETER FSM// + /////////////////////////// + + //These registers are immediately updated + reg [CNT_SIZE -1:0] drop_cnt_r = 0; //Reg addr = 0 + reg [CNT_SIZE -1:0] log_cnt_r = 0; //Reg addr = 1 + reg [DATA_WIDTH -1:0] inj_TDATA_r = 0; //Reg addr = 2 + reg inj_TVALID_r = 0; //Reg addr = 3 + reg inj_TLAST_r = 0; //Reg addr = 4 + reg [DATA_WIDTH/8 -1:0] inj_TKEEP_r = 0; //Reg addr = 5 + reg [DEST_WIDTH -1:0] inj_TDEST_r = 0; //Reg addr = 6 + reg [ID_WIDTH -1:0] inj_TID_r = 0; //Reg addr = 7 + reg keep_pausing_r = 0; //Reg addr = 8 + reg keep_logging_r = 0; //Reg addr = 9 + reg keep_dropping_r = 0; //Reg addr = 10 + + `localparam CMD_FSM_ADDR = 0; + `localparam CMD_FSM_DATA = 1; + `localparam CMD_FSM_IGNORE = 2; + + reg [1:0] cmd_fsm_state = CMD_FSM_ADDR; + reg [REG_ADDR_WIDTH -1:0] saved_reg_addr = 0; + + //The user puts in a reg address of all ones to commit register values + wire reg_addr_all_ones = (cmd_reg_addr == {REG_ADDR_WIDTH{1'b1}}); + wire latch_sig = (cmd_fsm_state == CMD_FSM_ADDR) && msg_for_us && cmd_in_TVALID && reg_addr_all_ones; + + //In the interests of keeping things simple, the commands will happen over + //two flits: "address" and "data" + + //TODO: If Vivado is truly inefficient, I'll rewrite this code in a more + //optimized way +`genif (NO_RST && STICKY_MODE == 0) begin + always @(posedge clk) begin + if (latch_sig) begin //This code is unlikely to synthesize to something efficient + drop_cnt_r <= 0; + log_cnt_r <= 0; + inj_TVALID_r <= 0; + keep_pausing_r <= 0; + keep_logging_r <= 0; + keep_dropping_r <= 0; + end else if (cmd_in_TVALID) begin + case (cmd_fsm_state) + CMD_FSM_ADDR: begin + cmd_fsm_state <= msg_for_us ? CMD_FSM_DATA : (reg_addr_all_ones ? CMD_FSM_ADDR : CMD_FSM_IGNORE); + saved_reg_addr <= cmd_reg_addr; + end CMD_FSM_DATA: begin + cmd_fsm_state <= CMD_FSM_ADDR; + case (saved_reg_addr) + 0: drop_cnt_r <= cmd_in_TDATA[CNT_SIZE -1:0]; + 1: log_cnt_r <= cmd_in_TDATA[CNT_SIZE -1:0]; + 2: inj_TDATA_r <= cmd_in_TDATA; + 3: inj_TVALID_r <= cmd_in_TDATA[0]; + 4: inj_TLAST_r <= cmd_in_TDATA[0]; + 5: inj_TKEEP_r <= cmd_in_TDATA[DATA_WIDTH/8 -1:0]; + 6: inj_TDEST_r <= cmd_in_TDATA[DEST_WIDTH -1:0]; + 7: inj_TID_r <= cmd_in_TDATA[ID_WIDTH -1:0]; + 8: keep_pausing_r <= cmd_in_TDATA[0]; + 9: keep_logging_r <= cmd_in_TDATA[0]; + 10: keep_dropping_r <= cmd_in_TDATA[0]; + endcase + end CMD_FSM_IGNORE: begin + cmd_fsm_state <= CMD_FSM_ADDR; + end + endcase + end + end +`else_genif (NO_RST) begin //NO_RST && STICKY_MODE == 1 + always @(posedge clk) begin + if (cmd_in_TVALID) begin + case (cmd_fsm_state) + CMD_FSM_ADDR: begin + cmd_fsm_state <= reg_addr_all_ones ? + CMD_FSM_ADDR : + (msg_for_us ? CMD_FSM_DATA : CMD_FSM_IGNORE); + + saved_reg_addr <= cmd_reg_addr; + end CMD_FSM_DATA: begin + cmd_fsm_state <= CMD_FSM_ADDR; + case (saved_reg_addr) + 0: drop_cnt_r <= cmd_in_TDATA[CNT_SIZE -1:0]; + 1: log_cnt_r <= cmd_in_TDATA[CNT_SIZE -1:0]; + 2: inj_TDATA_r <= cmd_in_TDATA; + 3: inj_TVALID_r <= cmd_in_TDATA[0]; + 4: inj_TLAST_r <= cmd_in_TDATA[0]; + 5: inj_TKEEP_r <= cmd_in_TDATA[DATA_WIDTH/8 -1:0]; + 6: inj_TDEST_r <= cmd_in_TDATA[DEST_WIDTH -1:0]; + 7: inj_TID_r <= cmd_in_TDATA[ID_WIDTH -1:0]; + 8: keep_pausing_r <= cmd_in_TDATA[0]; + 9: keep_logging_r <= cmd_in_TDATA[0]; + 10: keep_dropping_r <= cmd_in_TDATA[0]; + endcase + end CMD_FSM_IGNORE: begin + cmd_fsm_state <= CMD_FSM_ADDR; + end + endcase + end + end +`else_genif (STICKY_MODE == 0) begin //HAS_RST && STICKY_MODE == 0 + always @(posedge clk) begin + if (latch_sig || rst_sig) begin + drop_cnt_r <= 0; + log_cnt_r <= 0; + inj_TVALID_r <= 0; + keep_pausing_r <= 0; + keep_logging_r <= 0; + keep_dropping_r <= 0; + end else if (cmd_in_TVALID) begin + case (cmd_fsm_state) + CMD_FSM_ADDR: begin + cmd_fsm_state <= msg_for_us ? CMD_FSM_DATA : (reg_addr_all_ones ? CMD_FSM_ADDR : CMD_FSM_IGNORE); + saved_reg_addr <= cmd_reg_addr; + end CMD_FSM_DATA: begin + cmd_fsm_state <= CMD_FSM_ADDR; + case (saved_reg_addr) + 0: drop_cnt_r <= cmd_in_TDATA[CNT_SIZE -1:0]; + 1: log_cnt_r <= cmd_in_TDATA[CNT_SIZE -1:0]; + 2: inj_TDATA_r <= cmd_in_TDATA; + 3: inj_TVALID_r <= cmd_in_TDATA[0]; + 4: inj_TLAST_r <= cmd_in_TDATA[0]; + 5: inj_TKEEP_r <= cmd_in_TDATA[DATA_WIDTH/8 -1:0]; + 6: inj_TDEST_r <= cmd_in_TDATA[DEST_WIDTH -1:0]; + 7: inj_TID_r <= cmd_in_TDATA[ID_WIDTH -1:0]; + 8: keep_pausing_r <= cmd_in_TDATA[0]; + 9: keep_logging_r <= cmd_in_TDATA[0]; + 10: keep_dropping_r <= cmd_in_TDATA[0]; + endcase + end CMD_FSM_IGNORE: begin + cmd_fsm_state <= CMD_FSM_ADDR; + end + endcase + end + end +`else_gen //HAS_RST && STICKY_MODE == 1 + always @(posedge clk) begin + if (rst_sig) begin + drop_cnt_r <= 0; + log_cnt_r <= 0; + inj_TVALID_r <= 0; + keep_pausing_r <= 0; + keep_logging_r <= 0; + keep_dropping_r <= 0; + end else if (cmd_in_TVALID) begin + case (cmd_fsm_state) + CMD_FSM_ADDR: begin + cmd_fsm_state <= reg_addr_all_ones ? + CMD_FSM_ADDR : + (msg_for_us ? CMD_FSM_DATA : CMD_FSM_IGNORE); + + saved_reg_addr <= cmd_reg_addr; + end CMD_FSM_DATA: begin + cmd_fsm_state <= CMD_FSM_ADDR; + case (saved_reg_addr) + 0: drop_cnt_r <= cmd_in_TDATA[CNT_SIZE -1:0]; + 1: log_cnt_r <= cmd_in_TDATA[CNT_SIZE -1:0]; + 2: inj_TDATA_r <= cmd_in_TDATA; + 3: inj_TVALID_r <= cmd_in_TDATA[0]; + 4: inj_TLAST_r <= cmd_in_TDATA[0]; + 5: inj_TKEEP_r <= cmd_in_TDATA[DATA_WIDTH/8 -1:0]; + 6: inj_TDEST_r <= cmd_in_TDATA[DEST_WIDTH -1:0]; + 7: inj_TID_r <= cmd_in_TDATA[ID_WIDTH -1:0]; + 8: keep_pausing_r <= cmd_in_TDATA[0]; + 9: keep_logging_r <= cmd_in_TDATA[0]; + 10: keep_dropping_r <= cmd_in_TDATA[0]; + endcase + end CMD_FSM_IGNORE: begin + cmd_fsm_state <= CMD_FSM_ADDR; + end + endcase + end + end +`endgen + + //////////////////////// + //GOVERNOR CONTROL FSM// + //////////////////////// + + reg [CNT_SIZE -1:0] drop_cnt = 0; + reg [CNT_SIZE -1:0] log_cnt = 0; + reg [DATA_WIDTH -1:0] inj_TDATA = 0; + reg inj_TVALID = 0; + reg inj_TLAST = 0; + reg [DATA_WIDTH/8 -1:0] inj_TKEEP = 0; + reg [DEST_WIDTH -1:0] inj_TDEST = 0; + reg [ID_WIDTH -1:0] inj_TID = 0; + reg keep_pausing = 0; + reg keep_logging = 0; + reg keep_dropping = 0; + + //Governor control wires. These feed directly into axis_governor + wire pause; + wire drop; + wire log_en; + +`genif (NO_RST) begin + always @(posedge clk) begin + if (latch_sig) begin + drop_cnt <= drop_cnt_r; + log_cnt <= log_cnt_r; + inj_TDATA <= inj_TDATA_r; + inj_TVALID <= inj_TVALID_r; + inj_TLAST <= inj_TLAST_r; + inj_TKEEP <= inj_TKEEP_r; + inj_TDEST <= inj_TDEST_r; + inj_TID <= inj_TID_r; + keep_pausing <= keep_pausing_r; + keep_logging <= keep_logging_r; + keep_dropping <= keep_dropping_r; + end else begin + //Decrement drop_cnt when flit is sent (if drop_cnt is not already zero) + drop_cnt <= (|drop_cnt) ? (drop_cnt - `axis_flit(in)) : drop_cnt; + //Decrement drop_cnt when flit is logged (if log_cnt is not already zero) + log_cnt <= (|log_cnt) ? (log_cnt - `axis_flit(log)) : log_cnt; + //Set inj_TVALID to 0 once an injection occurs + inj_TVALID <= `axis_flit(inj) ? 0 : inj_TVALID; + end + end +`else_gen + always @(posedge clk) begin + if (rst_sig) begin + drop_cnt <= 0; + log_cnt <= 0; + inj_TDATA <= 0; + inj_TVALID <= 0; + inj_TLAST <= 0; + inj_TKEEP <= 0; + inj_TDEST <= 0; + inj_TID <= 0; + keep_pausing <= 0; + keep_logging <= 0; + keep_dropping <= 0; + end else if (latch_sig) begin + drop_cnt <= drop_cnt_r; + log_cnt <= log_cnt_r; + inj_TDATA <= inj_TDATA_r; + inj_TVALID <= inj_TVALID_r; + inj_TLAST <= inj_TLAST_r; + inj_TKEEP <= inj_TKEEP_r; + inj_TDEST <= inj_TDEST_r; + inj_TID <= inj_TID_r; + keep_pausing <= keep_pausing_r; + keep_logging <= keep_logging_r; + keep_dropping <= keep_dropping_r; + end else begin + //Decrement drop_cnt when flit is sent (if drop_cnt is not already zero) + drop_cnt <= (|drop_cnt) ? (drop_cnt - `axis_flit(in)) : drop_cnt; + //Decrement drop_cnt when flit is logged (if log_cnt is not already zero) + log_cnt <= (|log_cnt) ? (log_cnt - `axis_flit(log)) : log_cnt; + //Set inj_TVALID to 0 once an injection occurs + inj_TVALID <= `axis_flit(inj) ? 0 : inj_TVALID; + end + end +`endgen + + //We don't pause if our dropping/logging counters are still going + assign pause = keep_pausing && ~(|drop_cnt || |log_cnt); + assign log_en = keep_logging || (|log_cnt); + assign drop = keep_dropping || (|drop_cnt); + + + ///////////////////////////// + //INSTANTIATE AXIS GOVERNOR// + ///////////////////////////// + + axis_governor #( + .DATA_WIDTH(DATA_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .ID_WIDTH(ID_WIDTH) + ) ello_guvna ( + .clk(clk), + + //Input AXI Stream. + .in_TDATA(in_TDATA), + .in_TVALID(in_TVALID), + .in_TREADY(in_TREADY), + .in_TKEEP(in_TKEEP), + .in_TDEST(in_TDEST), + .in_TID(in_TID), + .in_TLAST(in_TLAST), + + //Inject AXI Stream. + .inj_TDATA(inj_TDATA), + .inj_TVALID(inj_TVALID), + .inj_TREADY(inj_TREADY), + .inj_TKEEP(inj_TKEEP), + .inj_TDEST(inj_TDEST), + .inj_TID(inj_TID), + .inj_TLAST(inj_TLAST), + + //Output AXI Stream. + .out_TDATA(out_TDATA), + .out_TVALID(out_TVALID), + .out_TREADY(out_TREADY), + .out_TKEEP(out_TKEEP), + .out_TDEST(out_TDEST), + .out_TID(out_TID), + .out_TLAST(out_TLAST), + + //Log AXI Stream. + .log_TDATA(log_TDATA), + .log_TVALID(log_TVALID), + .log_TREADY(log_TREADY), + .log_TKEEP(log_TKEEP), + .log_TDEST(log_TDEST), + .log_TID(log_TID), + .log_TLAST(log_TLAST), + + //Control signals + .pause(pause), + .drop(drop), + .log_en(log_en) + ); + + ////////////////////////////////////// + //CONNECT REMAINING WIRES TO OUTPUTS// + ////////////////////////////////////// + +`genif (PIPE_STAGE) begin + //Delay by one cycle for timing + reg [DATA_WIDTH -1:0] cmd_out_TDATA_r = 0; + reg cmd_out_TVALID_r = 0; + + always @(posedge clk) begin + cmd_out_TDATA_r <= cmd_in_TDATA; + cmd_out_TVALID_r <= cmd_in_TVALID; + end + + assign cmd_out_TDATA = cmd_out_TDATA_r; + assign cmd_out_TVALID = cmd_out_TVALID_r; +`else_gen + assign cmd_out_TDATA = cmd_in_TDATA; + assign cmd_out_TVALID = cmd_in_TVALID; +`endgen + + //Run the log outputs through the "headerizer" module. This module takes a + //single flit and sends out a two-flit packet: + // + // Input flit: log_TDATA, log_TKEEP, log_TLAST, log_TDEST, log_TID, log_TUSER + // + // Output flit 1: TDATA = {log_TLAST, log_TDEST, log_TID, log_TUSER}, TKEEP = (all ones), TLAST = 0 + // Output flit 2: TDATA = log_TDATA, TKEEP = log_TKEEP, TLAST = log_TLAST + + `wire_axis_kl(log_with_hdr, DATA_WIDTH); + + axis_headerizer # ( + .DATA_WIDTH(DATA_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .ID_WIDTH(ID_WIDTH), + .USER_WIDTH(ADDR_WIDTH), + .RESET_TYPE(RESET_TYPE), + .ENABLE_TLAST_HACK(1) + ) headerizer ( + .clk(clk), + .rst(rst), + + `inst_axis_kl(sides, log), + .sides_TDEST(log_TDEST), + .sides_TID(log_TID), + .sides_TUSER(ADDR), + + `inst_axis_kl(hdr, log_with_hdr) + ); + + assign log_catted_TDATA = {log_with_hdr_TDATA, log_with_hdr_TKEEP}; + assign log_catted_TVALID = log_with_hdr_TVALID; + assign log_with_hdr_TREADY = log_catted_TREADY; + assign log_catted_TLAST = log_with_hdr_TLAST; + + //It might be better to just concat the log side channels into TDATA rather + //than in a header flit. That way, at the cost of more routing resources, + //it might be possible to have 100% transparent snooping. I wasn't sure + //what to do so I just picked the header method and went with it + +endmodule diff --git a/middleware/hls/dbg_guv/dbg_guv_drivers.mem b/middleware/hls/dbg_guv/dbg_guv_drivers.mem new file mode 100644 index 00000000..fbb7cffa --- /dev/null +++ b/middleware/hls/dbg_guv/dbg_guv_drivers.mem @@ -0,0 +1,66 @@ +cmd_in_TDATA cmd_in_TVALID Comments +0000 0 +0000 0 +0000 0 + +0008 1 Select dbg_core[0]::keep_pausing +0000 0 +0001 1 Enable keep_pausing +000F 1 Latch +0000 0 +0000 0 +0000 0 + +0008 1 Select dbg_core[0]::keep_pausing +0000 0 +0000 1 Enable keep_pausing +0000 0 +000F 1 Latch +0000 0 +0000 0 + + +0018 1 Select dbg_core[1]::log_cnt +0001 1 Set to 1 +0011 1 Select dbg_core[1]::keep_pausing +0001 1 Enable keep_pausing +0000 0 +001F 1 Latch (performs single step) +0000 0 +0000 0 +0000 0 +001F 1 Latch (performs single step if STICKY_MODE == 1, otherwise, disables keep_pausing) +0000 0 +0000 0 +0000 0 +001F 1 Latch (performs single step if STICKY_MODE == 1, otherwise, disables keep_pausing) +0000 0 +0000 0 +0000 0 + + + + +0012 1 Select dbg_core[1]::inj_TDATA +0055 1 Set to 0x55 +0013 1 Select dbg_core[1]::inj_TVALID +0001 1 Set to 1 +0011 1 Select dbg_core[1]::keep_pausing +0001 1 Disable keep_pausing +0000 0 +001F 1 Latch (performs single injection) +001F 0 Latch (but command not valid!!!!) +001F 0 Latch (but command not valid!!!!) +001F 0 Latch (but command not valid!!!!) +001F 0 Latch (but command not valid!!!!) +001F 0 Latch (but command not valid!!!!) +001F 0 Latch (but command not valid!!!!) +001F 0 Latch (but command not valid!!!!) +001F 0 Latch (but command not valid!!!!) +001F 0 Latch (but command not valid!!!!) +001F 0 Latch (but command not valid!!!!) +0000 0 +0000 0 +0000 0 + +0000 0 diff --git a/middleware/hls/dbg_guv/dbg_guv_tb.v b/middleware/hls/dbg_guv/dbg_guv_tb.v new file mode 100644 index 00000000..884b0fc1 --- /dev/null +++ b/middleware/hls/dbg_guv/dbg_guv_tb.v @@ -0,0 +1,228 @@ +`timescale 1ns / 1ps + +/* +dbg_guv_tb.v + +A testbench for the AXIS governor controller. + +In case you're wondering, no, I don't painstakingly write everything in my +testbenches by hand. I mostly copy and paste and use Geany's regex find/replace +tool. + +For example, + +Find: + .* (\w+)\, + +Replace: + \t\t\.\1\(\1\)\, + +is really handy for doing up module instantiations +*/ + +`ifdef ICARUS_VERILOG +`include "dbg_guv.v" +`endif + +`include "macros.vh" + +module dbg_guv_tb # ( + parameter DATA_WIDTH = 64, + parameter DEST_WIDTH = 16, + parameter ID_WIDTH = 16, + parameter CNT_SIZE = 16, + parameter ADDR_WIDTH = 12, //This gives 1024 simultaneous debug cores + parameter ADDR = 0, //Set this to be different for each + parameter RESET_TYPE = `NO_RESET, + parameter STICKY_MODE = 1, //If 1, latching registers does not reset them + parameter PIPE_STAGE = 0 //This causes a delay on cmd_out in case fanout is + //an issue +); + reg clk = 0; + reg rst = 0; + + //Input command stream + //This may be a bad decision, but I decided the command width should match + //the inject data width. + //How does this work if you have a bunch of streams of different sizes that + //you want to debug? Also, what about the rr_tree in that case? + //Also, this core cannot assert backpressure + reg [DATA_WIDTH -1:0] cmd_in_TDATA = 0; + reg cmd_in_TVALID = 0; + + //All the controllers are daisy-chained. If in incoming command is not for + //this controller, send it to the next one + wire [DATA_WIDTH -1:0] cmd_out_TDATA; + wire cmd_out_TVALID; + + //Also,since this module is intended to wrap around axis_governor, we need + //to provide access to its ports through this one. + + //Input1 AXI Stream. + reg [DATA_WIDTH-1:0] in1_TDATA = 0; + reg in1_TVALID = 1; + wire in1_TREADY; + reg [DATA_WIDTH/8 -1:0] in1_TKEEP = 0; + reg [DEST_WIDTH -1:0] in1_TDEST = 0; + reg [ID_WIDTH -1:0] in1_TID = 0; + reg in1_TLAST = 0; + + //Input2 AXI Stream. + reg [DATA_WIDTH-1:0] in2_TDATA = 1; + reg in2_TVALID = 1; + wire in2_TREADY; + reg [DATA_WIDTH/8 -1:0] in2_TKEEP = 1; + reg [DEST_WIDTH -1:0] in2_TDEST = 1; + reg [ID_WIDTH -1:0] in2_TID = 1; + reg in2_TLAST = 0; + + //Output1 AXI Stream. + wire [DATA_WIDTH-1:0] out1_TDATA; + wire out1_TVALID; + reg out1_TREADY = 1; + wire [DATA_WIDTH/8 -1:0] out1_TKEEP; + wire [DEST_WIDTH -1:0] out1_TDEST; + wire [ID_WIDTH -1:0] out1_TID; + wire out1_TLAST; + + //Output2 AXI Stream. + wire [DATA_WIDTH-1:0] out2_TDATA; + wire out2_TVALID; + reg out2_TREADY = 1; + wire [DATA_WIDTH/8 -1:0] out2_TKEEP; + wire [DEST_WIDTH -1:0] out2_TDEST; + wire [ID_WIDTH -1:0] out2_TID; + wire out2_TLAST; + + //Log1 AXI Stream. + //This core takes care of concatting the sidechannels into the data part + //of the flit + `sim_out_axis_l(log_catted1, DATA_WIDTH + DATA_WIDTH/8); + + //Log2 AXI Stream. + //This core takes care of concatting the sidechannels into the data part + //of the flit + `sim_out_axis_l(log_catted2, DATA_WIDTH + DATA_WIDTH/8); + + integer fd, dummy; + + initial begin + $dumpfile("dbg_guv.vcd"); + $dumpvars; + $dumplimit(512000); + end + + always #5 clk <= ~clk; + + always @(posedge clk) begin + if (`axis_flit(in1)) begin + in1_TDATA <= in1_TDATA + 2; + in1_TKEEP = ((1 << ($random & 32'b111)) -1); + in1_TLAST = $random; + in1_TDEST = $random; + in1_TID = $random; + end + if (`axis_flit(in2)) begin + in2_TDATA <= in2_TDATA + 2; + in2_TKEEP = ((1 << ($random & 32'b111)) -1); + in2_TLAST = $random; + in2_TDEST = $random; + in2_TID = $random; + end + end + + //Wires from ctl1 to ctl2 + wire [DATA_WIDTH -1:0] cmd12_TDATA; + wire cmd12_TVALID; + + dbg_guv # ( + .DATA_WIDTH(DATA_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .ID_WIDTH(ID_WIDTH), + .CNT_SIZE(CNT_SIZE), + .ADDR_WIDTH(ADDR_WIDTH), //This gives 1024 simultaneous debug cores + .ADDR(0), //Set this to be different for each + .RESET_TYPE(RESET_TYPE), + .STICKY_MODE(STICKY_MODE), //If 1, latching registers does not reset them + .PIPE_STAGE(PIPE_STAGE) //This causes a delay on cmd_out in case fanout is + //an issue + ) ctl1 ( + .clk(clk), + .rst(rst), + + .cmd_in_TDATA(cmd_in_TDATA), + .cmd_in_TVALID(cmd_in_TVALID), + + .cmd_out_TDATA(cmd12_TDATA), + .cmd_out_TVALID(cmd12_TVALID), + + //Input AXI Stream. + .in_TDATA(in1_TDATA), + .in_TVALID(in1_TVALID), + .in_TREADY(in1_TREADY), + .in_TKEEP(in1_TKEEP), + .in_TDEST(in1_TDEST), + .in_TID(in1_TID), + .in_TLAST(in1_TLAST), + + //Output AXI Stream. + .out_TDATA(out1_TDATA), + .out_TVALID(out1_TVALID), + .out_TREADY(out1_TREADY), + .out_TKEEP(out1_TKEEP), + .out_TDEST(out1_TDEST), + .out_TID(out1_TID), + .out_TLAST(out1_TLAST), + + //Log AXI Stream. + //This core takes care of concatting the sidechannels into the data part + //of the flit + `inst_axis_l(log_catted, log_catted1) + ); + + dbg_guv # ( + .DATA_WIDTH(DATA_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .ID_WIDTH(ID_WIDTH), + .CNT_SIZE(CNT_SIZE), + .ADDR_WIDTH(ADDR_WIDTH), //This gives 1024 simultaneous debug cores + .ADDR(1), //Set this to be different for each + .RESET_TYPE(RESET_TYPE), + .STICKY_MODE(STICKY_MODE), //If 1, latching registers does not reset them + .PIPE_STAGE(PIPE_STAGE) //This causes a delay on cmd_out in case fanout is + //an issue + ) ctl2 ( + .clk(clk), + .rst(rst), + + .cmd_in_TDATA(cmd12_TDATA), + .cmd_in_TVALID(cmd12_TVALID), + + .cmd_out_TDATA(cmd_out_TDATA), + .cmd_out_TVALID(cmd_out_TVALID), + + //Input AXI Stream. + .in_TDATA(in2_TDATA), + .in_TVALID(in2_TVALID), + .in_TREADY(in2_TREADY), + .in_TKEEP(in2_TKEEP), + .in_TDEST(in2_TDEST), + .in_TID(in2_TID), + .in_TLAST(in2_TLAST), + + //Output AXI Stream. + .out_TDATA(out2_TDATA), + .out_TVALID(out2_TVALID), + .out_TREADY(out2_TREADY), + .out_TKEEP(out2_TKEEP), + .out_TDEST(out2_TDEST), + .out_TID(out2_TID), + .out_TLAST(out2_TLAST), + + //Log AXI Stream. + //This core takes care of concatting the sidechannels into the data part + //of the flit + `inst_axis_l(log_catted, log_catted2) + ); + +endmodule diff --git a/middleware/hls/dbg_guv/ip_maker.tcl b/middleware/hls/dbg_guv/ip_maker.tcl new file mode 100644 index 00000000..8b258b5c --- /dev/null +++ b/middleware/hls/dbg_guv/ip_maker.tcl @@ -0,0 +1,79 @@ +# Call as: +# vivado -mode tcl -nolog -nojournal -source scripts/ip_package.tcl -tclargs $out_dir $ip_name $part_name + +set out_dir [lindex $argv 0] +set ip_name [lindex $argv 1] +set part_name [lindex $argv 2] +set project_name ${ip_name}_tmp_proj +create_project ${project_name} ${project_name} -part ${part_name} +add_files ${out_dir}/src +ipx::package_project -root_dir ${out_dir} -vendor mmerlini -library yov -taxonomy /UserIP + + +# Fix up 'rst' interface +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.RESET_TYPE')) = 1} [ipx::get_bus_interfaces rst -of_objects [ipx::current_core]] +ipx::add_bus_parameter POLARITY [ipx::get_bus_interfaces rst -of_objects [ipx::current_core]] +set_property VALUE ACTIVE_HIGH [ipx::get_bus_parameters POLARITY -of_objects [ipx::get_bus_interfaces rst -of_objects [ipx::current_core]]] + +# Add 'rstn' interface +ipx::add_bus_interface rstn [ipx::current_core] +set_property abstraction_type_vlnv xilinx.com:signal:reset_rtl:1.0 [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property bus_type_vlnv xilinx.com:signal:reset:1.0 [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.RESET_TYPE')) = 2} [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +ipx::add_port_map RST [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property physical_name rst [ipx::get_port_maps RST -of_objects [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]]] +ipx::add_bus_parameter POLARITY [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property VALUE ACTIVE_LOW [ipx::get_bus_parameters POLARITY -of_objects [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]]] + +# CNT_SIZE parameter +set_property display_name {Counter size} [ipgui::get_guiparamspec -name "CNT_SIZE" -component [ipx::current_core] ] +set_property tooltip {Width of drop_cnt and log_cnt} [ipgui::get_guiparamspec -name "CNT_SIZE" -component [ipx::current_core] ] +set_property widget {textEdit} [ipgui::get_guiparamspec -name "CNT_SIZE" -component [ipx::current_core] ] +set_property value 10 [ipx::get_user_parameters CNT_SIZE -of_objects [ipx::current_core]] +set_property value 10 [ipx::get_hdl_parameters CNT_SIZE -of_objects [ipx::current_core]] + +# ADDR_WIDTH parameter +set_property display_name {Address width} [ipgui::get_guiparamspec -name "ADDR_WIDTH" -component [ipx::current_core] ] +set_property tooltip {The width in bits of debug core addresses} [ipgui::get_guiparamspec -name "ADDR_WIDTH" -component [ipx::current_core] ] +set_property widget {textEdit} [ipgui::get_guiparamspec -name "ADDR_WIDTH" -component [ipx::current_core] ] +set_property value 8 [ipx::get_user_parameters ADDR_WIDTH -of_objects [ipx::current_core]] +set_property value 8 [ipx::get_hdl_parameters ADDR_WIDTH -of_objects [ipx::current_core]] + +# ADDR parameter +set_property display_name {Address} [ipgui::get_guiparamspec -name "ADDR" -component [ipx::current_core] ] +set_property tooltip {The unique address identifying this particular debug core} [ipgui::get_guiparamspec -name "ADDR" -component [ipx::current_core] ] +set_property widget {textEdit} [ipgui::get_guiparamspec -name "ADDR" -component [ipx::current_core] ] + +# RESET_TYPE parameter +set_property display_name {Reset type} [ipgui::get_guiparamspec -name "RESET_TYPE" -component [ipx::current_core] ] +set_property tooltip {} [ipgui::get_guiparamspec -name "RESET_TYPE" -component [ipx::current_core] ] +set_property widget {comboBox} [ipgui::get_guiparamspec -name "RESET_TYPE" -component [ipx::current_core] ] +set_property value_validation_type pairs [ipx::get_user_parameters RESET_TYPE -of_objects [ipx::current_core]] +set_property value_validation_pairs {None 0 {Active high} 1 {Active low} 2} [ipx::get_user_parameters RESET_TYPE -of_objects [ipx::current_core]] + +# STICKY_MODE parameter +set_property display_name {Enable sticky mode} [ipgui::get_guiparamspec -name "STICKY_MODE" -component [ipx::current_core] ] +set_property tooltip {When active, control registers are not reset to zero when latched} [ipgui::get_guiparamspec -name "STICKY_MODE" -component [ipx::current_core] ] +set_property widget {checkBox} [ipgui::get_guiparamspec -name "STICKY_MODE" -component [ipx::current_core] ] +set_property value true [ipx::get_user_parameters STICKY_MODE -of_objects [ipx::current_core]] +set_property value true [ipx::get_hdl_parameters STICKY_MODE -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_user_parameters STICKY_MODE -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_hdl_parameters STICKY_MODE -of_objects [ipx::current_core]] + +# PIPE_STAGE parameter +set_property display_name {Enable pipe stage} [ipgui::get_guiparamspec -name "PIPE_STAGE" -component [ipx::current_core] ] +set_property tooltip {Adds a pipe stage to reduce fanout on command stream daisy chain} [ipgui::get_guiparamspec -name "PIPE_STAGE" -component [ipx::current_core] ] +set_property widget {checkBox} [ipgui::get_guiparamspec -name "PIPE_STAGE" -component [ipx::current_core] ] +set_property value true [ipx::get_user_parameters PIPE_STAGE -of_objects [ipx::current_core]] +set_property value true [ipx::get_hdl_parameters PIPE_STAGE -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_user_parameters PIPE_STAGE -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_hdl_parameters PIPE_STAGE -of_objects [ipx::current_core]] + +ipx::create_xgui_files [ipx::current_core] +ipx::update_checksums [ipx::current_core] +ipx::save_core [ipx::current_core] +close_project +exit + + + diff --git a/middleware/hls/marcos_macros/README.txt b/middleware/hls/marcos_macros/README.txt new file mode 100644 index 00000000..284dbb46 --- /dev/null +++ b/middleware/hls/marcos_macros/README.txt @@ -0,0 +1,75 @@ +Now that I have a couple dozen Verilog projects under my belt, I've noticed a +number of things that I'm constantly redoing. So, obviously, I put them in a +Verilog header. + + +`localparam +----------- + +When I use iverilog in my Makefiles, I always add -DICARUS_VERILOG, and I'll +use that symbol in my preprocessor directives in my code. There's a small +number of things that must be done differently between Icarus and Vivado. + +Vivado allows the "localparam" type where Icarus does not. So, I use the +`localparam macro in my Verilog code, which gets defined to the right thing. + + +`logic +------ + +I don't normally use SystemVerilog because Vivado often chokes on it (at least +in my experience). However, one of its great features its the "logic" type; +unfortunately, in regular Verilog, sometimes you have to define an internal +wire as a "reg", because you wrote some code in an always @(*) block. The +signal is combinational, but Verilog syntax requires a "reg" type. + +Anyway, in SystemVerilog, you can just use "logic" in these cases. I define it +to be "reg" so that my Verilog code looks like the SystemVerilog "logic" type. + + +`CLOG2 +------ + +Don't overthink it. This gives you ceil(log_2(x)). + + +`in_axis and friends +-------------------- + +In my code I'm constantly finding myself writing AXI Stream signals. These +macros just make my life a little easier. For example, + + `in_axis_l(my_thing, 32) + +expands to + + input wire [32 -1:0] my_thing_TDATA, + input wire my_thing_TVALID, + output wire my_thing_TREADY, + input wire my_thing_TLAST + +There are a bunch of similar macros that have slightly different behaviour. + + +`axis_flit +---------- + +A little helper macro that expands to a predicate on an AXI Stream. This +predicate evaluates to true if that stream is sending a flit. + + +`NO_RESET, `ACTIVE_HIGH, and `ACTIVE LOW +---------------------------------------- + +In my newer Verilog modules I use a parameter to determine if it will have a +reset signal and what polarity it will use. This is usually worth the extra +trouble; sometimes removing reset logic can fix up timing failures. Also, I +really hate when reset polarities are inconsistent in your project. + + +`genif, `else_genif, `else_gen, and `endgen +------------------------------------------- + +Little macros that change the syntax of if-generate blocks. There's nothing +wrong with the original syntax, but I find this makes it easier to visually +spot places where I'm conditionally generating code. diff --git a/middleware/hls/marcos_macros/macros.vh b/middleware/hls/marcos_macros/macros.vh new file mode 100644 index 00000000..2f4aa25c --- /dev/null +++ b/middleware/hls/marcos_macros/macros.vh @@ -0,0 +1,223 @@ +`ifndef MACROS_VH_INCLUDE_GUARD +`define MACROS_VH_INCLUDE_GUARD 1 + +`ifdef ICARUS_VERILOG +`define localparam parameter +`else /*For Vivado*/ +`define localparam localparam +`endif + +`define logic reg + +`define CLOG2(x) (\ + (((x) <= 1) ? 0 : \ + (((x) <= 2) ? 1 : \ + (((x) <= 4) ? 2 : \ + (((x) <= 8) ? 3 : \ + (((x) <= 16) ? 4 : \ + (((x) <= 32) ? 5 : \ + (((x) <= 64) ? 6 : \ + (((x) <= 128) ? 7 : \ + (((x) <= 256) ? 8 : \ + (((x) <= 512) ? 9 : \ + (((x) <= 1024) ? 10 : \ + (((x) <= 2048) ? 11 : \ + (((x) <= 4096) ? 12 : \ + (((x) <= 8192) ? 13 : \ + (((x) <= 16384) ? 14 : \ + (((x) <= 32768) ? 15 : \ + (((x) <= 65536) ? 16 : \ + -1)))))))))))))))))) + +//Helper macros to declare AXI Streams nicely in Verilog +//The "_reg" versions are because Verilog forces you to use reg in always +//blocks (which is really awful!) +//The "sim_" versions are used when writing testbenches +`define in_axis(name, width) \ + input wire [width -1:0] name``_TDATA,\ + input wire name``_TVALID,\ + output wire name``_TREADY + +`define in_axis_reg(name, width) \ + input wire [width -1:0] name``_TDATA,\ + input wire name``_TVALID,\ + output reg name``_TREADY + +`define out_axis(name, width) \ + output wire [width -1:0] name``_TDATA,\ + output wire name``_TVALID,\ + input wire name``_TREADY + +`define out_axis_reg(name, width) \ + output reg [width -1:0] name``_TDATA,\ + output reg name``_TVALID,\ + input wire name``_TREADY + +`define wire_axis(name, width) \ + wire [width -1:0] name``_TDATA;\ + wire name``_TVALID;\ + wire name``_TREADY + +`define sim_in_axis(name, width) \ + reg [width -1:0] name``_TDATA = 0;\ + reg name``_TVALID = 0;\ + wire name``_TREADY + +`define sim_out_axis(name, width) \ + wire [width -1:0] name``_TDATA;\ + wire name``_TVALID;\ + reg name``_TREADY = 1 + +`define ports_axis(name) name``_TDATA, name``_TVALID, name``_TREADY + +`define inst_axis(lname, rname) \ + .lname``_TDATA(rname``_TDATA),\ + .lname``_TVALID(rname``_TVALID),\ + .lname``_TREADY(rname``_TREADY) + +//Same, but with TLAST signal +`define in_axis_l(name, width) \ + `in_axis(name, width),\ + input wire name``_TLAST + +`define in_axis_l_reg(name, width) \ + `in_axis_reg(name, width),\ + input wire name``_TLAST + +`define out_axis_l(name, width) \ + `out_axis(name, width),\ + output wire name``_TLAST + +`define out_axis_l_reg(name, width) \ + `out_axis_reg(name, width),\ + output reg name``_TLAST + +`define wire_axis_l(name, width) \ + `wire_axis(name, width);\ + wire name``_TLAST + +`define sim_in_axis_l(name, width) \ + `sim_in_axis(name, width);\ + reg name``_TLAST = 0 + +`define sim_out_axis_l(name, width) \ + `sim_out_axis(name, width);\ + wire name``_TLAST + +`define ports_axis_l(name) `ports_axis(name), name``_TLAST + +`define inst_axis_l(lname, rname) \ + `inst_axis(lname, rname),\ + .lname``_TLAST(rname``_TLAST) + +//Same, but with TKEEP signal +`define in_axis_k(name, width) \ + `in_axis(name, width),\ + input wire [(width/8) -1:0] name``_TKEEP + +`define in_axis_k_reg(name, width) \ + `in_axis_reg(name, width),\ + input wire [(width/8) -1:0] name``_TKEEP + +`define out_axis_k(name, width) \ + `out_axis(name, width),\ + output wire [(width/8) -1:0] name``_TKEEP + +`define out_axis_k_reg(name, width) \ + `out_axis_reg(name, width),\ + output reg [(width/8) -1:0] name``_TKEEP + +`define wire_axis_k(name, width) \ + `wire_axis(name, width);\ + wire [(width/8) -1:0] name``_TKEEP + +`define sim_in_axis_k(name, width) \ + `sim_in_axis(name, width);\ + reg [(width/8) -1:0] name``_TKEEP = 0 + +`define sim_out_axis_k(name, width) \ + `sim_out_axis(name, width);\ + wire [(width/8) -1:0] name``_TKEEP + +`define ports_axis_k(name) `ports_axis(name), name``_TKEEP + +`define inst_axis_k(lname, rname) \ + `inst_axis(lname, rname),\ + .lname``_TKEEP(rname``_TKEEP) + +//Same, but with TLAST and TKEEP signals +`define in_axis_kl(name, width) \ + `in_axis_l(name, width),\ + input wire [(width/8) -1:0] name``_TKEEP +`define in_axis_lk(n, w) `in_axis_kl(n, w) + +`define in_axis_kl_reg(name, width) \ + `in_axis_l_reg(name, width),\ + input wire [(width/8) -1:0] name``_TKEEP +`define in_axis_lk_reg(n, w) `in_axis_kl_reg(n, w) + +`define out_axis_kl(name, width) \ + `out_axis_l(name, width),\ + output wire [(width/8) -1:0] name``_TKEEP +`define out_axis_lk(n, w) `out_axis_kl(n, w) + +`define out_axis_kl_reg(name, width) \ + `out_axis_l_reg(name, width),\ + output reg [(width/8) -1:0] name``_TKEEP +`define out_axis_lk_reg(n, w) `out_axis_kl_reg(n, w) + +`define wire_axis_kl(name, width) \ + `wire_axis_l(name, width);\ + wire [(width/8) -1:0] name``_TKEEP +`define wire_axis_lk(n, w) `wire_axis_kl(n, w) + +`define sim_in_axis_kl(name, width) \ + `sim_in_axis_l(name, width);\ + reg [(width/8) -1:0] name``_TKEEP = 0 +`define sim_in_axis_lk(n, w) `sim_in_axis_kl(n, w) + +`define sim_out_axis_kl(name, width) \ + `sim_out_axis_l(name, width);\ + wire [(width/8) -1:0] name``_TKEEP +`define sim_out_axis_lk(n, w) `sim_out_axis_kl(n, w) + +`define ports_axis_kl(name) `ports_axis_l(name), name``_TKEEP +`define ports_axis_lk(name) `ports_axis_kl(name) + +`define inst_axis_kl(lname, rname) \ + `inst_axis_l(lname, rname),\ + .lname``_TKEEP(rname``_TKEEP) +`define inst_axis_lk(lname, rname) `inst_axis_kl(lname, rname) + +//Some helper macros to neaten up code dealing with AXI Streams VALID and READY +`define axis_flit(name) (name``_TVALID && name``_TREADY) +`define axis_last(name) (name``_TVALID && name``_TREADY && name``_TLAST) + +`define NO_RESET 0 +`define ACTIVE_HIGH 1 +`define ACTIVE_LOW 2 + +`define genif generate if +`define else_genif end else if +`define else_gen end else begin +`define endgen end endgenerate + +//Common construct I use in my desgins +`define wire_rst_sig \ + wire rst_sig; \ +`genif (RESET_TYPE == `ACTIVE_HIGH) begin \ + assign rst_sig = rst; \ +end else begin \ + assign rst_sig = ~rst; \ +`endgen \ +wire unused_dummy_in_wire_rst_sig_macro + +//This makes the ternary operator look a little more friendly +//"if" was already taken +`define si(x) ((x) ? +`define prendre ( +`define autrement ) : ( +`define fin )) + + +`endif diff --git a/middleware/hls/rr4/Makefile b/middleware/hls/rr4/Makefile new file mode 100644 index 00000000..ace273a1 --- /dev/null +++ b/middleware/hls/rr4/Makefile @@ -0,0 +1,54 @@ +# Edit the following four variables and run `make`. +# After that, follow the instructions in ip_maker.tcl +# +# Alternatively, you could set any of these variables on the command line: +# +# $ make src_dir=/path/to/my/src dst_dir=/path/to/my/output +# +# Just make sure that you don't include a trailing slash on your directories + +dst_dir=$(GALAPAGOS_PATH)/hlsBuild/$(GALAPAGOS_BOARD_NAME)/ip +src_dir=. +ip_name=rr4 +part_no=$(GALAPAGOS_PART) + + +# Makes Makefile easier to read +out_dir=${dst_dir}/${ip_name} + +MODULE := rr4 + + +default: ip + +tb: $(MODULE).vcd + +$(MODULE).vcd: $(MODULE).vvp + vvp $(MODULE).vvp + +$(MODULE).vvp: $(MODULE).v $(MODULE)_tb.v $(MODULE)_drivers.mem + iverilog -DICARUS_VERILOG -I../buffered_handshake -I../marcos_macros -o $(MODULE).vvp $(MODULE)_tb.v + +open: $(MODULE).vcd + gtkwave $(MODULE).vcd --autosavename & + +clean: + rm -rf $(MODULE).vvp + rm -rf $(MODULE).vcd + rm -rf ${out_dir} + rm -rf ${ip_name}_tmp_proj + +# Packages into a Vivado IP +ip: clean + rm -rf ${out_dir} + mkdir -p ${out_dir}/src + cp rr4.v ../marcos_macros/macros.vh ../buffered_handshake/bhand.v ${out_dir}/src + vivado -nolog -nojournal -notrace -mode batch -source ip_maker.tcl -tclargs ${out_dir} ${ip_name} ${part_no} + rm -rf ${ip_name}_tmp_proj + rm -f *log + rm -rf .Xil + rm -f vivado* + +force: + touch $(MODULE)_tb.v + make diff --git a/middleware/hls/rr4/ip_maker.tcl b/middleware/hls/rr4/ip_maker.tcl new file mode 100644 index 00000000..4debe809 --- /dev/null +++ b/middleware/hls/rr4/ip_maker.tcl @@ -0,0 +1,61 @@ +# Call as: +# vivado -mode tcl -nolog -nojournal -source scripts/ip_package.tcl -tclargs $out_dir $ip_name $part_name + +set out_dir [lindex $argv 0] +set ip_name [lindex $argv 1] +set part_name [lindex $argv 2] +set project_name ${ip_name}_tmp_proj +create_project ${project_name} ${project_name} -part ${part_name} +add_files ${out_dir}/src +ipx::package_project -root_dir ${out_dir} -vendor Marco_Merlini -library fpga_bpf -taxonomy /UserIP + +# DATA_WIDTH parameter GUI +set_property tooltip {Width of TDATA input} [ipgui::get_guiparamspec -name "DATA_WIDTH" -component [ipx::current_core] ] +set_property widget {textEdit} [ipgui::get_guiparamspec -name "DATA_WIDTH" -component [ipx::current_core] ] + +# PIPE_STAGE parameter GUI +set_property display_name {Enable pipeline registers} [ipgui::get_guiparamspec -name "PIPE_STAGE" -component [ipx::current_core] ] +set_property tooltip {Adds a cycle of latency to ease timing} [ipgui::get_guiparamspec -name "PIPE_STAGE" -component [ipx::current_core] ] +set_property widget {checkBox} [ipgui::get_guiparamspec -name "PIPE_STAGE" -component [ipx::current_core] ] +set_property value true [ipx::get_user_parameters PIPE_STAGE -of_objects [ipx::current_core]] +set_property value true [ipx::get_hdl_parameters PIPE_STAGE -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_user_parameters PIPE_STAGE -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_hdl_parameters PIPE_STAGE -of_objects [ipx::current_core]] + +# RESET_TYPE parameter GUI +set_property display_name {Reset type} [ipgui::get_guiparamspec -name "RESET_TYPE" -component [ipx::current_core] ] +set_property widget {comboBox} [ipgui::get_guiparamspec -name "RESET_TYPE" -component [ipx::current_core] ] +set_property value 1 [ipx::get_user_parameters RESET_TYPE -of_objects [ipx::current_core]] +set_property value 1 [ipx::get_hdl_parameters RESET_TYPE -of_objects [ipx::current_core]] +set_property value_validation_type pairs [ipx::get_user_parameters RESET_TYPE -of_objects [ipx::current_core]] +set_property value_validation_pairs {None 0 {Active high} 1 {Active low} 2} [ipx::get_user_parameters RESET_TYPE -of_objects [ipx::current_core]] + +# TLAST_ARB parameter GUI +set_property display_name {Arbitrate on TLAST} [ipgui::get_guiparamspec -name "TLAST_ARB" -component [ipx::current_core] ] +set_property tooltip {} [ipgui::get_guiparamspec -name "TLAST_ARB" -component [ipx::current_core] ] +set_property widget {checkBox} [ipgui::get_guiparamspec -name "TLAST_ARB" -component [ipx::current_core] ] +set_property value true [ipx::get_user_parameters TLAST_ARB -of_objects [ipx::current_core]] +set_property value true [ipx::get_hdl_parameters TLAST_ARB -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_user_parameters TLAST_ARB -of_objects [ipx::current_core]] +set_property value_format bool [ipx::get_hdl_parameters TLAST_ARB -of_objects [ipx::current_core]] + +# Clean up 'rst' interface bus +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.RESET_TYPE')) = 1} [ipx::get_bus_interfaces rst -of_objects [ipx::current_core]] +ipx::add_bus_parameter POLARITY [ipx::get_bus_interfaces rst -of_objects [ipx::current_core]] +set_property VALUE ACTIVE_HIGH [ipx::get_bus_parameters POLARITY -of_objects [ipx::get_bus_interfaces rst -of_objects [ipx::current_core]]] + +# Create 'rstn' interface bus +ipx::add_bus_interface rstn [ipx::current_core] +set_property abstraction_type_vlnv xilinx.com:signal:reset_rtl:1.0 [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property bus_type_vlnv xilinx.com:signal:reset:1.0 [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property enablement_dependency {spirit:decode(id('MODELPARAM_VALUE.RESET_TYPE')) = 2} [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +ipx::add_port_map RST [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property physical_name rst [ipx::get_port_maps RST -of_objects [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]]] +ipx::add_bus_parameter POLARITY [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]] +set_property VALUE ACTIVE_LOW [ipx::get_bus_parameters POLARITY -of_objects [ipx::get_bus_interfaces rstn -of_objects [ipx::current_core]]] + +ipx::create_xgui_files [ipx::current_core] +ipx::update_checksums [ipx::current_core] +ipx::save_core [ipx::current_core] +close_project +exit diff --git a/middleware/hls/rr4/rr4.gtkw b/middleware/hls/rr4/rr4.gtkw new file mode 100644 index 00000000..65d4bf30 --- /dev/null +++ b/middleware/hls/rr4/rr4.gtkw @@ -0,0 +1,72 @@ +[*] +[*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI +[*] Wed Feb 12 22:40:13 2020 +[*] +[dumpfile] "/home/mahkoe/research/ye_olde_verilogge/rr4/rr4.vcd" +[dumpfile_mtime] "Wed Feb 12 22:39:54 2020" +[dumpfile_size] 11115 +[savefile] "/home/mahkoe/research/ye_olde_verilogge/rr4/rr4.gtkw" +[timestart] 40200000 +[size] 1916 1060 +[pos] -1 -1 +*-15.595535 40405000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] rr4_tb. +[treeopen] rr4_tb.DUT. +[treeopen] rr4_tb.DUT.genblk10. +[sst_width] 214 +[signals_width] 134 +[sst_expanded] 1 +[sst_vpaned_height] 314 +@28 +rr4_tb.clk +@800200 +-o +@24 +[color] 2 +rr4_tb.s0_cnt[15:0] +[color] 2 +rr4_tb.s1_cnt[15:0] +[color] 2 +rr4_tb.s2_cnt[15:0] +[color] 2 +rr4_tb.s3_cnt[15:0] +rr4_tb.o_TDATA[7:0] +@28 +rr4_tb.o_TVALID +rr4_tb.o_TREADY +@24 +rr4_tb.who[1:0] +@28 +rr4_tb.o_flit +rr4_tb.o_TLAST +@1000200 +-o +@800200 +-s0 +@24 +rr4_tb.s0_TDATA[7:0] +@28 +rr4_tb.s0_TVALID +rr4_tb.s0_TREADY +rr4_tb.s0_TLAST +@1000200 +-s0 +@800200 +-s1 +@22 +rr4_tb.s1_TDATA[7:0] +@28 +rr4_tb.s1_TVALID +rr4_tb.s1_TREADY +rr4_tb.s1_TLAST +@1000200 +-s1 +@28 +rr4_tb.DUT.req[3:0] +@29 +rr4_tb.DUT.sel[3:0] +@28 +rr4_tb.DUT.sel_r[3:0] +rr4_tb.DUT.gnt[3:0] +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/middleware/hls/rr4/rr4.v b/middleware/hls/rr4/rr4.v new file mode 100644 index 00000000..7f4a53e1 --- /dev/null +++ b/middleware/hls/rr4/rr4.v @@ -0,0 +1,207 @@ +/* +A four-to-one round-robin AXI Stream Switch. + +Parameters: + DATA_WIDTH - Width of TDATA field + RESET_TYPE - Set to 0 for no reset, 1 for active-high, 2 for active-low + PIPE_STAGE - Set to 1 to enable output pipeline registers + TLAST_ARB - Set to 1 to only arbitrate on TLAST + +To use this in IP Integrator, you might have to concat your sidechannels into +your TDATA. The axis_tdata_concat core is a nice way to do this +*/ + +`ifdef ICARUS_VERILOG +`include "bhand.v" //Make sure to use -I switch in iverilog command +`endif + +`include "macros.vh" + +module rr4 # ( + parameter DATA_WIDTH = 32, + parameter PIPE_STAGE = 1, + parameter RESET_TYPE = `NO_RESET, + parameter TLAST_ARB = 1 +) ( + input wire clk, + input wire rst, + + //If not arbitrating on TLAST, the TLAST inputs are ignored + `in_axis_l(s0, DATA_WIDTH), + `in_axis_l(s1, DATA_WIDTH), + `in_axis_l(s2, DATA_WIDTH), + `in_axis_l(s3, DATA_WIDTH), + + `out_axis_l(o, DATA_WIDTH) +); + + //State variables used for deciding who gets the grant signal + reg [3:0] sel_r = 'b0001; + wire [3:0] sel; + reg undecided = 1; + + //////////////// + //HELPER WIRES// + //////////////// + + wire [3:0] req = {s3_TVALID, s2_TVALID, s1_TVALID, s0_TVALID}; + //This technique found in Altera's "Advanced synthesis cookbook" + wire [3:0] base = {sel_r[2:0], sel_r[3]}; + wire [7:0] req_dbl = {req, req}; + wire [7:0] gnt_dbl = req_dbl & ~(req_dbl - base); + wire [3:0] gnt = gnt_dbl[7:4] | gnt_dbl[3:0]; + + //Vivado requires this wire to be forward-declared in order to compile the + //rest of it, even if it would be perfectly logical + wire rst_sig; +`genif (RESET_TYPE == `ACTIVE_HIGH) begin + assign rst_sig = rst; +`else_genif (RESET_TYPE == `ACTIVE_LOW) begin + assign rst_sig = ~rst; +`endgen + + ////////////// + //MAIN LOGIC// + ////////////// + + //Use multiplexer + `wire_axis_l(muxout, DATA_WIDTH); + mux4_onehot # (DATA_WIDTH) the_mux ( + sel, + `ports_axis_l(s0), + `ports_axis_l(s1), + `ports_axis_l(s2), + `ports_axis_l(s3), + `ports_axis_l(muxout) + ); + + //Update selection (taking care to only arbitrate on TLAST, if that's what + //was required. There are four cases: + // Arbitrate on TLAST and no reset + // Arbitrate on TLAST and reset is active high or active low + // No TLAST and no reset + // No TLAST but reset is active high or active low +`genif(TLAST_ARB && RESET_TYPE == `NO_RESET) begin + assign sel = undecided ? gnt : sel_r; + + always @(posedge clk) begin + sel_r <= `axis_flit(muxout) ? sel : sel_r; + undecided <= `axis_flit(muxout) ? (muxout_TLAST ? 1 : 0) : undecided; + end +`else_genif(TLAST_ARB) begin + assign sel = undecided ? gnt : sel_r; + + always @(posedge clk) begin + if (rst_sig) begin + sel_r <= 'b0001; + undecided <= 1; + end else begin + sel_r <= `axis_flit(muxout) ? sel : sel_r; + undecided <= `axis_flit(muxout) ? (muxout_TLAST ? 1 : 0) : undecided; + end + end +`else_genif(RESET_TYPE == `NO_RESET) begin + assign sel = gnt; + always @(posedge clk) begin + sel_r <= `axis_flit(muxout) ? sel : sel_r; + end +`else_gen + assign sel = gnt; + always @(posedge clk) begin + if (rst_sig) begin + sel_r <= 'b0001; + end else begin + sel_r <= `axis_flit(muxout) ? sel : sel_r; + end + end +`endgen + + ////////////////// + //ASSIGN OUTPUTS// + ////////////////// + + //If the pipeline registers are on, we need a bhand +`genif (PIPE_STAGE) begin + bhand # ( + .DATA_WIDTH(DATA_WIDTH + 1), //Need room for TLAST + .RESET_TYPE(RESET_TYPE) + ) sit_shake_good_boy ( + .clk(clk), + .rst(rst), + + .idata({muxout_TDATA, muxout_TLAST}), + .idata_vld(muxout_TVALID), + .idata_rdy(muxout_TREADY), + + .odata({o_TDATA, o_TLAST}), + .odata_vld(o_TVALID), + .odata_rdy(o_TREADY) + ); +`else_gen + assign o_TDATA = muxout_TDATA; + assign o_TVALID = muxout_TVALID; + assign muxout_TREADY = o_TREADY; + assign o_TLAST = muxout_TLAST; +`endgen + +endmodule + + +//Combinational +module mux4_onehot # ( + parameter DATA_WIDTH = 32 +) ( + input wire [3:0] sel, + + `in_axis_l_reg(s0, DATA_WIDTH), + `in_axis_l_reg(s1, DATA_WIDTH), + `in_axis_l_reg(s2, DATA_WIDTH), + `in_axis_l_reg(s3, DATA_WIDTH), + + `out_axis_l_reg(o, DATA_WIDTH) +); + + wire [1:0] muxsel = {sel[3] | sel[2], sel[3] | sel[1]}; + + always @(*) begin + case (muxsel) + 'b00: begin + o_TDATA <= s0_TDATA; + o_TVALID <= s0_TVALID; + o_TLAST <= s0_TLAST; + s0_TREADY <= o_TREADY; + s1_TREADY <= 0; + s2_TREADY <= 0; + s3_TREADY <= 0; + end + 'b01: begin + o_TDATA <= s1_TDATA; + o_TVALID <= s1_TVALID; + o_TLAST <= s1_TLAST; + s0_TREADY <= 0; + s1_TREADY <= o_TREADY; + s2_TREADY <= 0; + s3_TREADY <= 0; + end + 'b10: begin + o_TDATA <= s2_TDATA; + o_TVALID <= s2_TVALID; + o_TLAST <= s2_TLAST; + s0_TREADY <= 0; + s1_TREADY <= 0; + s2_TREADY <= o_TREADY; + s3_TREADY <= 0; + end + 'b11: begin + o_TDATA <= s3_TDATA; + o_TVALID <= s3_TVALID; + o_TLAST <= s3_TLAST; + s0_TREADY <= 0; + s1_TREADY <= 0; + s2_TREADY <= 0; + s3_TREADY <= o_TREADY; + end + endcase + end + +endmodule diff --git a/middleware/hls/rr4/rr4_drivers.mem b/middleware/hls/rr4/rr4_drivers.mem new file mode 100644 index 00000000..f4f08092 --- /dev/null +++ b/middleware/hls/rr4/rr4_drivers.mem @@ -0,0 +1 @@ +Nothing to see here... diff --git a/middleware/hls/rr4/rr4_tb.v b/middleware/hls/rr4/rr4_tb.v new file mode 100644 index 00000000..b4179f75 --- /dev/null +++ b/middleware/hls/rr4/rr4_tb.v @@ -0,0 +1,190 @@ +`timescale 1ns / 1ps + +/* +testbench_template.v + +Replace innards with desired logic +*/ + +`include "rr4.v" +`include "macros.vh" + +module rr4_tb # ( + parameter DATA_WIDTH = 8, + parameter PIPE_STAGE = 1, + parameter RESET_TYPE = `NO_RESET, + parameter TLAST_ARB = 1 +); + reg clk = 0; + reg rst = 0; + //Other variables connected to your instance + `sim_in_axis_l(s0, DATA_WIDTH); + `sim_in_axis_l(s1, DATA_WIDTH); + `sim_in_axis_l(s2, DATA_WIDTH); + `sim_in_axis_l(s3, DATA_WIDTH); + `sim_out_axis_l(o, DATA_WIDTH); + + //Makes sim easier to read + wire [1:0] who = o_TDATA[1:0]; + wire o_flit = `axis_flit(o); + + //A few simple checks to make sure the outputs look okay. + reg [DATA_WIDTH -1:0] s0_prev = 0; + reg [DATA_WIDTH -1:0] s1_prev = 1; + reg [DATA_WIDTH -1:0] s2_prev = 2; + reg [DATA_WIDTH -1:0] s3_prev = 3; + + reg [15:0] s0_cnt = 0; + reg [15:0] s1_cnt = 0; + reg [15:0] s2_cnt = 0; + reg [15:0] s3_cnt = 0; + + integer fd, dummy; + + initial begin + $dumpfile("rr4.vcd"); + $dumpvars; + $dumplimit(512000); + + s0_TDATA = 4; + s1_TDATA = 5; + s2_TDATA = 6; + s3_TDATA = 7; + + s0_TVALID = 0; + s1_TVALID = 0; + s2_TVALID = 0; + s3_TVALID = 0; + + s0_TLAST = 0; + s1_TLAST = 0; + s2_TLAST = 0; + s3_TLAST = 0; + + fd = $fopen("rr4_drivers.mem", "r"); + if (fd == 0) begin + $display("Could not open file"); + $finish; + end + + while ($fgetc(fd) != "\n") begin + if ($feof(fd)) begin + $display("Error: file is in incorrect format"); + $finish; + end + end + end + + always #5 clk <= ~clk; + + always @(posedge clk) begin + if ($feof(fd)) begin + $display("Reached end of drivers file"); + #20 + $finish; + end + + //#0.01 + //dummy = $fscanf(fd, "%F%O%R%M%A%T", /* list of variables */); + #200 + $dumpoff; + #40000 + $dumpon; + #200 + $finish; + end + + //Quick and dirty test vectors + always @(posedge clk) begin + if (`axis_flit(s0)) begin + s0_cnt <= s0_cnt + s0_TLAST; + s0_TDATA <= s0_TDATA + 4; + end if (`axis_flit(s1)) begin + s1_cnt <= s1_cnt + s1_TLAST; + s1_TDATA <= s1_TDATA + 4; + end if (`axis_flit(s2)) begin + s2_cnt <= s2_cnt + s2_TLAST; + s2_TDATA <= s2_TDATA + 4; + end if (`axis_flit(s3)) begin + s3_cnt <= s3_cnt + s3_TLAST; + s3_TDATA <= s3_TDATA + 4; + end + + s0_TVALID <= $random; + s1_TVALID <= $random; + s2_TVALID <= $random; + s3_TVALID <= $random; + //~ s0_TVALID <= 1; + //~ s1_TVALID <= 1; + //~ s2_TVALID <= 1; + //~ s3_TVALID <= 1; + + s0_TLAST <= $random; + s1_TLAST <= $random; + s2_TLAST <= $random; + s3_TLAST <= $random; + //~ s0_TLAST <= `axis_flit(s0) ? ~s0_TLAST : s0_TLAST; + //~ s1_TLAST <= `axis_flit(s1) ? ~s1_TLAST : s1_TLAST; + //~ s2_TLAST <= `axis_flit(s2) ? ~s2_TLAST : s2_TLAST; + //~ s3_TLAST <= `axis_flit(s3) ? ~s3_TLAST : s3_TLAST; + + o_TREADY <= $random; + //~ o_TREADY <= 1; + end + + + `define IDLE 3'b111 + reg [2:0] state = `IDLE; + + //Sime simple checks on the output + always @(posedge clk) begin + //This makes sure no flits are dropped or repeated + if (`axis_flit(o)) begin + case (who) + 2'b00: begin + if (o_TDATA - s0_prev != 8'd4) $display("Error! cur = %d, prev = %d", o_TDATA, s0_prev); + s0_prev <= o_TDATA; + end 2'b01: begin + if (o_TDATA - s1_prev != 8'd4) $display("Error! cur = %d, prev = %d", o_TDATA, s1_prev); + s1_prev <= o_TDATA; + end 2'b10: begin + if (o_TDATA - s2_prev != 8'd4) $display("Error! cur = %d, prev = %d", o_TDATA, s2_prev); + s2_prev <= o_TDATA; + end 2'b11: begin + if (o_TDATA - s3_prev != 8'd4) $display("Error! cur = %d, prev = %d", o_TDATA, s3_prev); + s3_prev <= o_TDATA; + end + endcase + end + + //This makes sure no packet is interrupted before TLAST + if (`axis_flit(o)) begin + if (state != `IDLE && state[1:0] != who[1:0]) begin + $display("Error! Interrupted packet!"); + end + if (o_TLAST) begin + state <= `IDLE; + end else if (state == `IDLE) begin + state <= {1'b0, who}; + end + end + end + + rr4 # ( + .DATA_WIDTH(DATA_WIDTH), + .PIPE_STAGE(PIPE_STAGE), + .RESET_TYPE(RESET_TYPE), + .TLAST_ARB(TLAST_ARB) + ) DUT ( + clk, rst, + + `ports_axis_l(s0), + `ports_axis_l(s1), + `ports_axis_l(s2), + `ports_axis_l(s3), + + `ports_axis_l(o) + ); + + +endmodule diff --git a/middleware/hls/rr4/rr_tree.tcl b/middleware/hls/rr4/rr_tree.tcl new file mode 100644 index 00000000..6c210d53 --- /dev/null +++ b/middleware/hls/rr4/rr_tree.tcl @@ -0,0 +1,36 @@ +# This proc automates the creation of a tree of RR4 modules + +proc rr_tree {msts} { + startgroup + set nnodes [expr ([llength $msts]+1)/3] + + set slvs {} + set nodes {} + while {$nnodes > 0} { + set node [create_bd_cell -vlnv Marco_Merlini:fpga_bpf:rr4 -name node_$nnodes] + lappend nodes $node + lappend slvs [get_bd_intf_pins $node/s0] + lappend slvs [get_bd_intf_pins $node/s1] + lappend slvs [get_bd_intf_pins $node/s2] + lappend slvs [get_bd_intf_pins $node/s3] + incr nnodes -1 + if {$nnodes > 0} { + lappend msts [get_bd_intf_pins $node/o ] + } + } + + while {[llength $msts] > 0} { + connect_bd_intf_net [lindex $msts 0] [lindex $slvs 0] + set msts [lreplace $msts 0 0] + set slvs [lreplace $slvs 0 0] + } + + set h [create_bd_cell -type hier -name rr_tree] + + move_bd_cells $h $nodes + + create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:axis_rtl:1.0 rr_tree/o + connect_bd_intf_net [get_bd_intf_pins rr_tree/o] [get_bd_intf_pins rr_tree/node_1/o] + + endgroup +} diff --git a/middleware/python/cluster.py b/middleware/python/cluster.py index 02e8cc17..eae3586c 100644 --- a/middleware/python/cluster.py +++ b/middleware/python/cluster.py @@ -8,6 +8,8 @@ import os import socket, struct import glob +# MM Mar 3 / 2020 Needed OrderedDict for compatibility with xmltodict output +from collections import OrderedDict class cluster(abstractDict): """ This class is the top-level interface to the myriad other objects used @@ -107,6 +109,8 @@ def __init__(self, name, kernel_file, map_file, mode='file'): if(mode=='file'): logical_dict = self.getDict(kernel_file)['cluster']['kernel'] map_dict = self.getDict(map_file)['cluster']['node'] + if not isinstance(map_dict, list): + map_dict = [map_dict] else: logical_dict = kernel_file['cluster']['kernel'] map_dict = map_file['cluster']['node'] @@ -119,6 +123,11 @@ def __init__(self, name, kernel_file, map_file, mode='file'): if 'rep' in kern_dict: # Automatically gives sequential numbers base_num = int(kern_dict['num']) + # MM Mar 3 / 2020 Added error message if user tries to use + # number 0, which is reserved for the debug logic + if (base_num == 0): + raise Exception("ERROR: Kernel number 0 is reserved for debug logic. Please renumber your kernels\n\n") + for i in range(0, int(kern_dict['rep'])): kern_dict_local = copy.deepcopy(kern_dict) # Set number for this kernel instance @@ -182,24 +191,69 @@ def __init__(self, name, kernel_file, map_file, mode='file'): else: kern_dict_local['rep'] = 1 kern_dict_local['num'] = int(kern_dict_local['num']) + # MM Mar 3 / 2020 Added error message if user tries to use + # number 0, which is reserved for the debug logic + if (kern_dict_local['num'] == 0): + raise Exception("ERROR: Kernel number 0 is reserved for debug logic. Please renumber your kernels\n\n") + self.kernels.append(kernel(**kern_dict_local)) - + + # At this point, self.kernels contains a list of dicts with properly + # converted info about each kernel + ## Dumps kernel info to the screen (printf debugging) - #for kern in self.kernels: - # print("kernel object " + str(kern.data)) - + # print("KERNEL INFO") + # for kern in self.kernels: + # print("kernel object " + str(kern.data)) + + # MM Mar 3 / 2020 Add a placeholder kernel into each node. This causes + # the other Galapagos code to make the switches large enough and to + # route all debug traffic to the right place. Later, we will remove the + # the placeholder and replace it with the correct debug interfaces + + placeholder_dbg_kernel = { + 'name': 'dbg_guv', + 'num': 0, + '#text': 'dbg_guv', + 'rep': 1, + 'clk': ['clk'], + 'aresetn': [], # I hope that works... + 'vendor': 'mmerlini', + 'lib': 'yov', + 'version': '1.0', + 'id_port': None, + 'm_axi': None, + 's_axi': None, + 's_axis': [OrderedDict([('scope', 'global'), ('name', 'cmd_in')])], + 'm_axis': [OrderedDict([('scope', 'global'), ('name', 'log_catted')])], + 'wire_master': None, + 'wire_slave': None, + 'ip': None, # TODO: Figure out what this should be + 'mac': None, # ditto + 'const': None, + 'properties': None, + 'board': None + } + # Now deal with nodes (i.e. a CPU or an FPGA) self.nodes = [] for node_idx, node_dict in enumerate(map_dict): + # This basically copies the dictionary parsed from the tags into another dictionary, # but it does also check the fields to make sure they're all valid and that no mandatory info # is missing node_inst = node(**node_dict) + # MM Mar 3 / 2020 Save a reference to the parent cluster in each node + node_inst.parent_cluster = self + # I'm fairly sure these next few lines of code are just converting # data formats node_inst['kernel'] = [] + # MM Mar 3 / 2020 Always put a placeholoder dbg_guv in each node + node_inst['kernel'].append(placeholder_dbg_kernel) + # for kmap_node in node_dict['kernel']: for kern_idx, kern in enumerate(self.kernels): diff --git a/middleware/python/node.py b/middleware/python/node.py index 12671eec..27cb0c6b 100644 --- a/middleware/python/node.py +++ b/middleware/python/node.py @@ -13,6 +13,11 @@ class node(abstractDict): mandatory, but there are a whole bunch of optional ones too. """ + # MM Mar 3 / 2020 Added a reference to the parent cluster into node, since + # I needed access to the packet description in tclFileGenerator.py. This was + # done in cluster.py after a node object is constructed by simply accessing + # node.parent_cluster + def __init__(self, address_space=32, **kwargs): """ Fills underlying dict representation with kwargs. Since this uses the call diff --git a/middleware/python/tclFileGenerator.py b/middleware/python/tclFileGenerator.py index 7d8820a5..7b87ccf6 100644 --- a/middleware/python/tclFileGenerator.py +++ b/middleware/python/tclFileGenerator.py @@ -868,6 +868,8 @@ def userApplicationRegionKernelConnectSwitches(outDir, tcl_user_app, sim): Now that the kernels, Galapagos router, and memory controllers are instantiated, it's time to connect them all together. + MM Feb 29/2020: All AXI Stream wires to kernels are now highlighted + Args: tcl_user_app: a tclMe object (which contains references to the FPGA's node object and a handle to the output file) @@ -909,7 +911,7 @@ def userApplicationRegionKernelConnectSwitches(outDir, tcl_user_app, sim): # Connect it to the correct port on the AXI switch (NOT directly into # the Galapagos router; there is an AXI stream switch IP between # the router and the kernel(s) ) - tcl_user_app.makeConnection( + tcl_user_app.makeMarkedConnection( 'intf', { 'name':'applicationRegion/input_switch', @@ -938,10 +940,11 @@ def userApplicationRegionKernelConnectSwitches(outDir, tcl_user_app, sim): 'port_name':'S01_AXIS' } ) - + + # Special case where there is only one kernel slave elif len(s_axis_array) == 1: if (sim == 1): - tcl_user_app.makeConnection( + tcl_user_app.makeMarkedConnection( 'intf', { 'name':'applicationRegion/arbiter', @@ -970,7 +973,7 @@ def userApplicationRegionKernelConnectSwitches(outDir, tcl_user_app, sim): # there's no input switch in this case if tcl_user_app.fpga['comm'] not in ['raw', 'none']: if 'custom' not in tcl_user_app.fpga or tcl_user_app.fpga['custom'] != 'GAScore': - tcl_user_app.makeConnection( + tcl_user_app.makeMarkedConnection( 'intf', { 'name':'applicationRegion/input_switch', @@ -1006,7 +1009,7 @@ def userApplicationRegionKernelConnectSwitches(outDir, tcl_user_app, sim): if tcl_user_app.fpga['comm'] not in ['raw', 'none']: instName = m_axis_array[0]['kernel_inst']['inst'] if 'custom' not in tcl_user_app.fpga or tcl_user_app.fpga['custom'] != 'GAScore': - tcl_user_app.makeConnection( + tcl_user_app.makeMarkedConnection( 'intf', { 'name': instName, @@ -1023,7 +1026,7 @@ def userApplicationRegionKernelConnectSwitches(outDir, tcl_user_app, sim): for idx, m_axis in enumerate(m_axis_array): instName = m_axis['kernel_inst']['inst'] idx_str = "%02d"%idx - tcl_user_app.makeConnection( + tcl_user_app.makeMarkedConnection( 'intf', { 'name': instName , @@ -1540,7 +1543,7 @@ def userApplicationRegion(outDir, fpga, sim): userApplicationRegionKernelConnectSwitches(outDir, tcl_user_app, sim) userApplicationRegionAssignAddresses(tcl_user_app, tcl_user_app.fpga['comm'] !='tcp' and tcl_user_app.fpga.address_space == 64) userApplicationLocalConnections(tcl_user_app) - + tcl_user_app.close() #return num_debug_interfaces @@ -1762,7 +1765,7 @@ def bridgeConnections(outDir, fpga, sim): else: instName = s_axis_array[0]['kernel_inst']['inst'] if tcl_bridge_connections.fpga['comm'] != 'none': - tcl_bridge_connections.makeConnection( + tcl_bridge_connections.makeMarkedConnection( 'intf', { 'name':'network/network_bridge_inst', @@ -1800,7 +1803,7 @@ def bridgeConnections(outDir, fpga, sim): else: instName = m_axis_array[0]['kernel_inst']['inst'] if tcl_bridge_connections.fpga['comm'] != 'none': - tcl_bridge_connections.makeConnection( + tcl_bridge_connections.makeMarkedConnection( 'intf', { 'name': instName, @@ -1980,6 +1983,38 @@ def bridgeConnections(outDir, fpga, sim): ) if tcl_bridge_connections.fpga['comm'] == 'none': tcl_custom.close() + + # MM Mar 2 / 2020 Added this call to one of my TCL scripts to wire up the + # custom debug cores + + galapagos_path = str(os.environ.get('GALAPAGOS_PATH')) + tcl_bridge_connections.addSource(galapagos_path + '/middleware/tclScripts/add_dbg_cores.tcl') + # Get the packet sizes. To avoid Verilog syntax errors, I intentionally make each width 1 if it was 0 + # (It's something I eventually want to fix, but Vivado's Verilog optimizer will remove the + # extra wires anyway) + c = fpga.parent_cluster + tdata_w = str(c.packet_data) + + tdest_w = c.packet_dest + if tdest_w == 0: + tdest_w = "1" + else: + tdest_w = str(tdest_w) + + tid_w = c.packet_id + if tid_w == 0: + tid_w = "1" + else: + tid_w = str(tid_w) + + # The last three positional arguments to this TCL function are the TDATA, TDEST, and TID widths + tcl_bridge_connections.tprint_raw('set g [add_dbg_core_to_list $marcos_list_of_dbg_nets 0 ' + tdata_w + ' ' + tdest_w + ' ' + tid_w + ']') + tcl_bridge_connections.tprint_raw('set first_cmd_in [lindex $g 0]') + tcl_bridge_connections.tprint_raw('set tree_out [lindex $g 1]') + + tcl_bridge_connections.addSource(galapagos_path + '/middleware/tclScripts/replace_dbg_placeholder.tcl') + tcl_bridge_connections.tprint_raw('replace_dbg_placeholder $first_cmd_in $tree_out') + tcl_bridge_connections.close() @@ -2029,6 +2064,7 @@ def makeTCLFiles(fpga, projectName, output_path, sim): if 'custom' in fpga: tclMain.addSource(outDir + '/' + str(fpga['num']) + '_custom.tcl') tclMain.addSource(galapagos_path + '/middleware/tclScripts/custom/' + fpga['custom'] + '.tcl') - + tclMain.tprint('validate_bd_design') + tclMain.tprint('save_bd_design') tclMain.close() diff --git a/middleware/python/tclMe.py b/middleware/python/tclMe.py index 4479c4eb..58b3633f 100644 --- a/middleware/python/tclMe.py +++ b/middleware/python/tclMe.py @@ -142,6 +142,46 @@ def makeConnection(self, conn_type, source, sink): elif sink['type'] == 'pin': self.tprint_raw('get_bd_pins ', end = '') self.tprint_raw(sink['name'] + '/' + sink['port_name'] + ']') - + + # MM Mar 2 / 2020: Copy-pasted makeConnection and added a few lines to + # save nets into a list called "marcos_list_of_dbg_nets" + def makeMarkedConnection(self, conn_type, source, sink): + src_port = "" + if source['type'] == 'port': + src_port += 'get_bd_ports ' + src_port += source['port_name'] + elif source['type'] == 'intf_port': + src_port += 'get_bd_intf_ports ' + src_port += source['port_name'] + elif source['type'] == 'intf': + src_port += 'get_bd_intf_pins ' + src_port += source['name'] + '/' + source['port_name'] + elif source['type'] == 'pin': + src_port += 'get_bd_pins ' + src_port += source['name'] + '/' + source['port_name'] + + if conn_type == 'net': + self.tprint('connect_bd_net [', end='') + elif conn_type == 'intf': + self.tprint('connect_bd_intf_net [', end='') + + self.tprint_raw(src_port, end = '] [') + + if sink['type'] == 'port': + self.tprint_raw('get_bd_ports ', end='') + self.tprint_raw(sink['port_name'] + ']') + elif sink['type'] == 'intf_port': + self.tprint_raw('get_bd_intf_ports ', end='') + self.tprint_raw(sink['port_name'] + ']') + elif sink['type'] == 'intf': + self.tprint_raw('get_bd_intf_pins ', end='') + self.tprint_raw(sink['name'] + '/' + sink['port_name'] + ']') + elif sink['type'] == 'pin': + self.tprint_raw('get_bd_pins ', end = '') + self.tprint_raw(sink['name'] + '/' + sink['port_name'] + ']') + + self.tprint_raw('set g [get_bd_intf_nets -of_objects [' + src_port, end=']]\n') + self.tprint_raw('lappend marcos_list_of_dbg_nets $g') + def close(self): self.fileHandle.close() diff --git a/middleware/tclScripts/add_dbg_cores.tcl b/middleware/tclScripts/add_dbg_cores.tcl new file mode 100644 index 00000000..592f980a --- /dev/null +++ b/middleware/tclScripts/add_dbg_cores.tcl @@ -0,0 +1,331 @@ +# These procs are used to automatically insert dbg_guvs into a design + +# Replaces the bd_intf_net n with a dbg_guv core named "inst" +proc add_dbg_core_to_net {n inst {tdata_width 64} {tdest_width 8} {tid_width 8}} { + puts "add_dbg_core_to_net args:" + puts "n = $n" + puts "inst = $inst" + puts "tdata_width = $tdata_width" + puts "tdest_width = $tdest_width" + puts "tid_width = $tid_width" + puts "----" + + # Get the two endpoints of this net + set pins [get_bd_intf_pins -of_objects $n -quiet] + set ports [get_bd_intf_ports -of_objects $n -quiet] + + # For some RIDICULOUS reason, a hierarchy port is both a port and a pin! + # So we do a little band-aid fix here and remove any element from pins + # that also appears in ports + foreach p $ports { + # see https://stackoverflow.com/questions/5701947/tcl-remove-an-element-from-a-list + set idx [lsearch $pins $p] + if {$idx != -1} { + set pins [lreplace $pins $idx $idx] + } + } + + set npins [llength $pins] + set nports [llength $ports] + + # Check if it has the right number of endpoints + if {[expr $npins + $nports] != 2} { + puts "Warning: invalid net" + return -1 + } + + # Name the two endpoints left and right + if {$npins == 2} { + set left [lindex $pins 0] + set right [lindex $pins 1] + } elseif {$npins == 1} { + set left [lindex $pins 0] + set right [lindex $ports 0] + } else { + set left [lindex $ports 0] + set right [lindex $ports 1] + } + + # Double-check that they are in fact AXI Stream + if {[string compare [get_property VLNV $left] "xilinx.com:interface:axis_rtl:1.0"] != 0} { + puts "Warning: this is not an AXI Stream interface" + return -1 + } + if {[string compare [get_property VLNV $right] "xilinx.com:interface:axis_rtl:1.0"] != 0} { + puts "Warning: this is not an AXI Stream interface" + return -1 + } + + # Quit early if there is already a dbg_guv at one endpoint of this net + if {$npins > 0} { + set left_vlnv [get_property VLNV [get_bd_cells -of_objects $left]] + if {$left_vlnv == "mmerlini:yov:dbg_guv:1.0"} { + puts "INFO: net already has a dbg_guv" + return 0 + } + } + + if {$npins == 2} { + set right_vlnv [get_property VLNV [get_bd_cells -of_objects $right]] + if {$right_vlnv == "mmerlini:yov:dbg_guv:1.0"} { + puts "INFO: net already has a dbg_guv" + return 0 + } + } + + # Delete the original net + delete_bd_objs $n + + # Instantiate the dbg_guv + # We do an UGLY hack to deal with Vivado's annoying rules about hierarchies + set prefix [lindex [regexp -inline {(.*)\/[^\/]*$} "[get_property PATH $n]"] 1] + set g [create_bd_cell -vlnv mmerlini:yov:dbg_guv $prefix/$inst] + + set_property -dict [list CONFIG.DATA_WIDTH $tdata_width CONFIG.DEST_WIDTH $tdest_width CONFIG.ID_WIDTH $tid_width] [get_bd_cells $g] + + # Connect the dbg_guv to the loose endpoints + if ![string compare [get_property MODE $left] Master] { + connect_bd_intf_net $g/out $right + connect_bd_intf_net $left $g/in + } else { + connect_bd_intf_net $g/out $left + connect_bd_intf_net $right $g/in + } + + return $g +} + +# Searches current BD to get next ID to use for a dbg_guv +proc get_next_dbg_core_id {} { + puts "get_next_dbg_core_id args:" + puts "----" + set cells [get_bd_cells -hierarchical -filter {VLNV == mmerlini:yov:dbg_guv:1.0} -quiet] + set next_id 0 + foreach c $cells { + set name [get_property NAME $c] + if {[string first "GUV_" $name] == 0} { + set idnum [string range $name 4 [string length $name]] + if [string is integer $idnum] { + set next_id [expr max($next_id,$idnum+1)] + } + } + } + return $next_id +} + +# Given a list of AXI Stream masters (with only TDATA, TREADY, TVALID, and TLAST) +# hooks up an automatically sized arbiter tree +# Returns a reference to the tree's final output +proc rr_tree {msts {data_w 64}} { + puts "rr_tree args:" + puts "msts = $msts" + puts "data_w = $data_w" + puts "----" + + # Quit gracefully if there is no tree to add + if {[llength $msts] == 0} { + return + } + + startgroup + # We need to keep track of the port at the root of the arbitration tree + # If the user called this function with only one dbg_guv, then no tree will + # be generated. Otherwise, if we go on to generate a tree, this variable + # will be updated + set arbiter_out [lindex $msts 0] + + # Number of rr4 nodes needed to construct the tree + set nnodes [expr ([llength $msts]+1)/3] + + set slvs {} + set nodes {} + while {$nnodes > 0} { + set node [create_bd_cell -vlnv Marco_Merlini:fpga_bpf:rr4 -name node_$nnodes] + + # Don't bother with reset lines. They only make timing harder to close, + # and for these little infrstructure IPs I don't really care + set_property CONFIG.RESET_TYPE 0 $node + # Set the width + set_property CONFIG.DATA_WIDTH [expr $data_w + $data_w/8] $node + + lappend nodes $node + lappend slvs [get_bd_intf_pins $node/s0] + lappend slvs [get_bd_intf_pins $node/s1] + lappend slvs [get_bd_intf_pins $node/s2] + lappend slvs [get_bd_intf_pins $node/s3] + incr nnodes -1 + if {$nnodes > 0} { + lappend msts [get_bd_intf_pins $node/o ] + } + } + + while {[llength $msts] > 0 && [llength $slvs] > 0} { + connect_bd_intf_net [lindex $msts 0] [lindex $slvs 0] + set msts [lreplace $msts 0 0] + set slvs [lreplace $slvs 0 0] + } + + if {[llength $nodes] > 0} { + # The user is not allowed to use this name + set h [create_bd_cell -type hier -name DBG_GUV_TREE] + + move_bd_cells $h $nodes + + create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:axis_rtl:1.0 DBG_GUV_TREE/o + connect_bd_intf_net [get_bd_intf_pins DBG_GUV_TREE/o] [get_bd_intf_pins DBG_GUV_TREE/node_1/o] + create_bd_pin -dir I DBG_GUV_TREE/clk + + connect_bd_net [list [get_bd_pins /DBG_GUV_TREE/*/clk] [get_bd_pins /DBG_GUV_TREE/clk]] + + # Update arbiter_out now that the tree is generated + set arbiter_out [get_bd_intf_pins /DBG_GUV_TREE/o] + } + endgroup + + # UGLY BAND-AID FIX: until I have a more robust method of handling generic + # choices for AXI Stream channel widths, I've added in an axis_unconcat to + # bridge the gap here + set uncat [create_bd_cell -type ip -vlnv mmerlini:yov:axis_unconcat:1.0 axis_unconcat_0] + set_property -dict [list CONFIG.DATA_WIDTH $data_w CONFIG.OUT_ENABLE_KEEP {true} CONFIG.IN_ENABLE_LAST {1} CONFIG.OUT_ENABLE_LAST {true}] $uncat + connect_bd_intf_net [get_bd_intf_pins $arbiter_out] [get_bd_intf_pins $uncat/left] + connect_bd_net [get_bd_pins /DBG_GUV_TREE/clk] [get_bd_pins $uncat/clk] + + return [get_bd_intf_pins $uncat/right] +} + + +proc del_dbg_core {c} { + startgroup + + set left [get_bd_intf_nets -of_objects [get_bd_intf_pins $c/in]] + set mst [get_bd_intf_pins -of_objects $left -filter "PATH !~ $c/in" -quiet] + if {[llength $mst] == 0} { + set mst [get_bd_intf_ports -of_objects $left -filter "PATH !~ $c/in" -quiet] + } + + set right [get_bd_intf_nets -of_objects [get_bd_intf_pins $c/out]] + set slv [get_bd_intf_pins -of_objects $right -filter "PATH !~ $c/out" -quiet] + if {[llength $slv] == 0} { + set slv [get_bd_intf_ports -of_objects $right -filter "PATH !~ $c/in" -quiet] + } + + delete_bd_objs [get_bd_intf_nets $left] [get_bd_intf_nets $right] + delete_bd_objs $c + + connect_bd_intf_net $mst $slv + + highlight_objects -color_index 3 [get_bd_intf_nets -of_objects $mst] + + endgroup +} + +proc del_all_dbg_cores {} { + startgroup + delete_bd_objs [get_bd_cells DBG_GUV_TREE -quiet] -quiet + set dbg_guvs [get_bd_cells -hierarchical -filter {VLNV =~ "*dbg_guv*"} -quiet] + foreach d $dbg_guvs { + set nets [get_bd_intf_nets -of_objects $d -quiet] + delete_bd_objs [get_bd_intf_nets $d/log_catted -quiet] -quiet + delete_bd_objs [get_bd_intf_nets $d/cmd_out -quiet] -quiet + del_dbg_core $d + } + endgroup +} + +# Given a list of nets, instrument each one with a dbg_guv. This code handles a +# number of common issues, such as not adding dbg_guvs if there is already one +# there, checking if the net is actually an AXI Stream, and of course, the +# frustrating special cases of port vs. pin +# The safe_mode argument will, when set to 1, delete all existing debug infra +# before adding in new cores. I only made this a parameter so that I could +# disable it in Galapagos's automatic generator; you normally want this to be on +# Finally, this proc returns a list containing the first cmd_in in the dbg_guv +# daisy chain, and the last output of the arbiter tree +proc add_dbg_core_to_list {nets {safe_mode 1} {tdata_width 64} {tdest_width 8} {tid_width 8}} { + puts "add_dbg_core_to_list args:" + puts "nets = $nets" + puts "safe_mode = $safe_mode" + puts "tdata_width = $tdata_width" + puts "tdest_width = $tdest_width" + puts "tid_width = $tid_width" + puts "----" + startgroup + + if {$safe_mode} { + del_all_dbg_cores + } + + # Stores the list of log outputs to run through the arbiter tree + set log_outs {} + + # Stores the previous cmd_out + set last_cmd {} + + # Stores the first cmd_in in the daisy chain + set first_cmd_in {} + + foreach n $nets { + # Choose an ID. get_next_dbg_core_id guarantees it will be unique, and + # if some dbg_guvs don't get added (e.g. if the highlighted net was + # invalid), it also makes sure that IDs don't get skipped + set next_id [get_next_dbg_core_id] + + # g holds a reference to the newly create dbg_guv cell + set g [add_dbg_core_to_net $n GUV_$next_id $tdata_width $tdest_width $tid_width] + if {$g == 0} { + # add_dbg_core_to_net (correctly) did not add a dbg_guv + continue + } elseif {$g == -1} { + # add_dbg_core_to_net encountered an error + puts "Warning: I can no longer guarantee that the debug cores will work! You may need to add them manually" + continue + } + + set_property CONFIG.ADDR $next_id $g + + # Add $g/log to the list of log outputs + lappend log_outs $g/log_catted + + # If last_cmd is not empty, connect its cmd_out to $g/cmd_in + if {[llength $last_cmd] == 1} { + connect_bd_intf_net [get_bd_intf_pins $last_cmd] [get_bd_intf_pins $g/cmd_in] + } else { + set first_cmd_in [get_bd_intf_pins $g/cmd_in] + } + + # Update last_cmd + set last_cmd $g/cmd_out + } + + # Put in the arbiter tree + set tree_out [rr_tree $log_outs $tdata_width] + + # Connect up the clocks + # TODO: If I ever plan to allow multiple clock domains, this will have to + # change + if {[llength $log_outs] > 1} { + connect_bd_net [list [get_bd_pins -of_objects [get_bd_cells -hierarchical -filter {VLNV == mmerlini:yov:dbg_guv:1.0}] -filter {NAME == clk}] [get_bd_pins /DBG_GUV_TREE/clk]] + } + endgroup + + return [list $first_cmd_in $tree_out] +} + +proc add_dbg_core_to_highlighted {{safe_mode 1} {tdata_width 64} {tdest_width 8} {tid_width 8}} { + add_dbg_core_to_list [get_bd_intf_nets [get_highlighted_objects]] $safe_mode $tdata_width $tdest_width $tid_width +} + +proc del_highlighted_dbg_cores {} { + startgroup + set cores [get_highlighted_objects] + foreach c $cores { + if {[get_property VLNV $c] != "mmerlini:yov:dbg_guv:1.0"} { + puts "Warning, trying to delete an IP which is not a dbg_guv" + continue + } + + del_dbg_core $c + + } + endgroup +} diff --git a/middleware/tclScripts/replace_dbg_placeholder.tcl b/middleware/tclScripts/replace_dbg_placeholder.tcl new file mode 100644 index 00000000..d407781d --- /dev/null +++ b/middleware/tclScripts/replace_dbg_placeholder.tcl @@ -0,0 +1,43 @@ +# This script is specific to Galapagos. + +# First of all, in the python middleware, I added a few lines that always put +# a placeholder dbg_guv IP in every node (with TDEST address 0). + +# Now that the placeholder is there and everything is wired up, it's time to +# replace it with what should go there. So this proc does three things: + +# 1. Save the port connected to placeholder/cmd_in as "in_port" and the port +# connected to placeholder/log_catted as "out_port" (see the code below) +# 2. Delete the placeholder core +# 3. Rewire in_port to connect to the golbal cmd_in of the dbg_guv daisy chain, +# and rewire out_port to connect to the output of the global dbg_guv log +# output + +proc replace_dbg_placeholder {first_cmd_in tree_out} { + puts "replace_dbg_placeholder args:" + puts "first_cmd_in = $first_cmd_in" + puts "tree_out = $tree_out" + puts "----" + # First, get a reference to the placeholder. This is really janky, but one + # consistent way to do is to assume that Galapagos named it "dbg_guv_inst_0" + set placeholder [get_bd_cells -hierarchical -filter {NAME == "dbg_guv_inst_0"}] + + # Get nets connected to the placeholder cell + set left [get_bd_intf_nets -of_objects [get_bd_intf_pins $placeholder/cmd_in]] + set right [get_bd_intf_nets -of_objects [get_bd_intf_pins $placeholder/log_catted]] + + # Now save references to the ports that were originally connected + set in_port [get_bd_intf_pins -of_objects $left -filter {PATH !~ *cmd_in}] + set out_port [get_bd_intf_pins -of_objects $right -filter {PATH !~ *log_catted}] + + # We can now safely delete the original placeholder + delete_bd_objs $placeholder + + # Rewire the input + connect_bd_intf_net [get_bd_intf_pins $first_cmd_in] $in_port + + # Rewire the output + connect_bd_intf_net [get_bd_intf_pins $tree_out] [get_bd_intf_pins $out_port] + + # It's ugly, but it works! +} diff --git a/setup.py b/setup.py index b2670d2a..d7d7bdb0 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ class directoryValidator(Validator): def validate(self, document): if not document.text=='': - if not os.path.isdir("/home/el"): + if not os.path.isdir(document.text): raise ValidationError( message='Please enter valid directory for galapagos path', cursor_position=len(document.text) diff --git a/shells/Makefile b/shells/Makefile index 01fd00a8..0c7f0c3d 100644 --- a/shells/Makefile +++ b/shells/Makefile @@ -13,24 +13,31 @@ guard-%: all: example synth = 0 +# This file does not exist... pr_tcl = ./hlsTest/dma_example/tclScripts/pr_bd.tcl +# This target is completely broken dma_example: vivado_hls ./hlsTest/dma_example/generate_hls.tcl vivado -mode gui -source ./tclScripts/make_shell.tcl -tclargs --project_name dma_example --pr_tcl ${pr_tcl} --start_synth ${synth} +# None of these files exist hlsExample: mkdir -p hlsBuild vivado_hls ./hlsTest/generate_hls.tcl ./hlsTest/generate.sh dma_example -example: hlsExample +example: mkdir -p projects - vivado -mode tcl -source ./tclScripts/make_shell.tcl -tclargs --project_name example --pr_tcl ${pr_tcl} --start_synth ${synth} + vivado -mode batch -source ./tclScripts/make_shell.tcl -tclargs --project_name example --start_synth ${synth} + # Get rid of references to nonexistent files + # vivado -mode tcl -source ./tclScripts/make_shell.tcl -tclargs --project_name example --pr_tcl ${pr_tcl} --start_synth ${synth} +# I think this is the target that gets called when you build a project app_dev: guard-PROJECTNAME mkdir -p projects vivado -mode gui -source ./tclScripts/make_shell.tcl -tclargs --project_name ${PROJECTNAME} + # Why do we always make a projects directory? clean: rm -rf projects/project_name diff --git a/shells/pynq-z2/tclScripts/shell_bd.tcl b/shells/pynq-z2/tclScripts/shell_bd.tcl index 7642bddb..e8ab8b1f 100644 --- a/shells/pynq-z2/tclScripts/shell_bd.tcl +++ b/shells/pynq-z2/tclScripts/shell_bd.tcl @@ -10,7 +10,7 @@ set script_folder [_tcl::get_script_folder] set_param synth.vivado.filterDuplicatedIPFiles 0 -set supported_versions {2017.4 2018.1 2018.2} +set supported_versions {2017.4 2018.1 2018.2 2018.3} namespace eval 2017.4 { set ip_list "\ @@ -48,6 +48,18 @@ namespace eval 2018.2 { " } +namespace eval 2018.3 { + set ip_list "\ + xilinx.com:ip:axi_bram_ctrl\ + xilinx.com:ip:axi_dma\ + xilinx.com:ip:axi_gpio\ + xilinx.com:ip:blk_mem_gen\ + xilinx.com:ip:proc_sys_reset\ + xilinx.com:ip:processing_system7\ + xilinx.com:ip:xlconcat\ + " +} + if { [info exists ::env(GALAPAGOS_PATH)] } { set root_path ${::env(GALAPAGOS_PATH)}/shells @@ -217,19 +229,19 @@ proc create_root_design { parentCell } { set sw [ create_bd_port -dir I -from 1 -to 0 sw ] # Create instance: axi_bram_ctrl_0, and set properties - set axi_bram_ctrl_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl:4.0 axi_bram_ctrl_0 ] + set axi_bram_ctrl_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl axi_bram_ctrl_0 ] set_property -dict [ list \ CONFIG.SINGLE_PORT_BRAM {1} \ ] $axi_bram_ctrl_0 # Create instance: axi_bram_ctrl_1, and set properties - set axi_bram_ctrl_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl:4.0 axi_bram_ctrl_1 ] + set axi_bram_ctrl_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl axi_bram_ctrl_1 ] set_property -dict [ list \ CONFIG.SINGLE_PORT_BRAM {1} \ ] $axi_bram_ctrl_1 # Create instance: axi_dma_0, and set properties - set axi_dma_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_dma:7.1 axi_dma_0 ] + set axi_dma_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_dma axi_dma_0 ] set_property -dict [ list \ CONFIG.c_include_mm2s_dre {1} \ CONFIG.c_include_s2mm_dre {1} \ @@ -242,14 +254,14 @@ proc create_root_design { parentCell } { ] $axi_dma_0 # Create instance: axi_gpio_0, and set properties - set axi_gpio_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio:2.0 axi_gpio_0 ] + set axi_gpio_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio axi_gpio_0 ] set_property -dict [ list \ CONFIG.C_ALL_OUTPUTS {1} \ CONFIG.C_GPIO_WIDTH {4} \ ] $axi_gpio_0 # Create instance: axi_gpio_1, and set properties - set axi_gpio_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio:2.0 axi_gpio_1 ] + set axi_gpio_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio axi_gpio_1 ] set_property -dict [ list \ CONFIG.C_ALL_INPUTS {1} \ CONFIG.C_ALL_OUTPUTS {0} \ @@ -258,7 +270,7 @@ proc create_root_design { parentCell } { ] $axi_gpio_1 # Create instance: axi_gpio_2, and set properties - set axi_gpio_2 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio:2.0 axi_gpio_2 ] + set axi_gpio_2 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio axi_gpio_2 ] set_property -dict [ list \ CONFIG.C_ALL_INPUTS {1} \ CONFIG.C_ALL_OUTPUTS {0} \ @@ -267,41 +279,41 @@ proc create_root_design { parentCell } { ] $axi_gpio_2 # Create instance: axi_interconnect_1, and set properties - set axi_interconnect_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_1 ] + set axi_interconnect_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect axi_interconnect_1 ] set_property -dict [ list \ CONFIG.NUM_MI {1} \ CONFIG.NUM_SI {2} \ ] $axi_interconnect_1 # Create instance: axi_interconnect_2, and set properties - set axi_interconnect_2 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_2 ] + set axi_interconnect_2 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect axi_interconnect_2 ] set_property -dict [ list \ CONFIG.NUM_MI {1} \ CONFIG.NUM_SI {1} \ ] $axi_interconnect_2 # Create instance: axi_interconnect_3, and set properties - set axi_interconnect_3 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_3 ] + set axi_interconnect_3 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect axi_interconnect_3 ] set_property -dict [ list \ CONFIG.NUM_MI {2} \ CONFIG.NUM_SI {1} \ ] $axi_interconnect_3 # Create instance: axi_interconnect_4, and set properties - set axi_interconnect_4 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_4 ] + set axi_interconnect_4 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect axi_interconnect_4 ] set_property -dict [ list \ CONFIG.NUM_MI {1} \ CONFIG.NUM_SI {1} \ ] $axi_interconnect_4 # Create instance: axi_interconnect_5, and set properties - set axi_interconnect_5 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_5 ] + set axi_interconnect_5 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect axi_interconnect_5 ] set_property -dict [ list \ CONFIG.NUM_MI {4} \ ] $axi_interconnect_5 # Create instance: blk_mem_gen_0, and set properties - set blk_mem_gen_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:blk_mem_gen:8.4 blk_mem_gen_0 ] + set blk_mem_gen_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:blk_mem_gen blk_mem_gen_0 ] set_property -dict [ list \ CONFIG.Enable_B {Use_ENB_Pin} \ CONFIG.Memory_Type {True_Dual_Port_RAM} \ @@ -312,10 +324,10 @@ proc create_root_design { parentCell } { ] $blk_mem_gen_0 # Create instance: proc_sys_reset_0, and set properties - set proc_sys_reset_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:proc_sys_reset:5.0 proc_sys_reset_0 ] + set proc_sys_reset_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:proc_sys_reset proc_sys_reset_0 ] # Create instance: ps7_0, and set properties - set ps7_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 ps7_0 ] + set ps7_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7 ps7_0 ] set_property -dict [ list \ CONFIG.PCW_ACT_APU_PERIPHERAL_FREQMHZ {650.000000} \ CONFIG.PCW_ACT_CAN0_PERIPHERAL_FREQMHZ {23.8095} \ @@ -1178,7 +1190,7 @@ proc create_root_design { parentCell } { ] $ps7_0 # Create instance: xlconcat_1, and set properties - set xlconcat_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat:2.1 xlconcat_1 ] + set xlconcat_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat xlconcat_1 ] set_property -dict [ list \ CONFIG.NUM_PORTS {4} \ ] $xlconcat_1 diff --git a/shells/sidewinder/tclScripts/shell_bd.tcl b/shells/sidewinder/tclScripts/shell_bd.tcl index 708b1abd..acb09226 100644 --- a/shells/sidewinder/tclScripts/shell_bd.tcl +++ b/shells/sidewinder/tclScripts/shell_bd.tcl @@ -120,6 +120,34 @@ namespace eval 2018.2 { " } +namespace eval 2018.3 { + set ip_list "\ + xilinx.com:ip:axi_bram_ctrl:4.1\ + xilinx.com:ip:axi_gpio:2.0\ + xilinx.com:ip:blk_mem_gen:8.4\ + xilinx.com:ip:clk_wiz:6.0\ + xilinx.com:ip:xlconstant:1.1\ + xilinx.com:ip:system_ila:1.1\ + xilinx.com:ip:util_vector_logic:2.0\ + xilinx.com:ip:vio:3.0\ + xilinx.com:ip:util_ds_buf:2.1\ + xilinx.com:ip:xdma:4.1\ + xilinx.com:ip:mdm:3.2\ + xilinx.com:ip:microblaze:11.0\ + xilinx.com:ip:proc_sys_reset:5.0\ + xilinx.com:ip:ddr4:2.2\ + xilinx.com:ip:axi_10g_ethernet:3.1\ + dlyma.org:dlyma:network_packet_fifo_rx:1.1\ + dlyma.org:dlyma:network_packet_fifo_tx:1.1\ + xilinx.com:ip:axis_register_slice:1.1\ + xilinx.com:ip:lmb_bram_if_cntlr:4.0\ + xilinx.com:ip:lmb_v10:3.0\ + xilinx.com:ip:fifo_generator:13.2\ + xilinx.com:ip:zynq_ultra_ps_e:3.2\ + xilinx.com:ip:xxv_ethernet:2.5 + " +} + # defines get_design_name if { [info exists ::env(GALAPAGOS_PATH)] } { source ${::env(GALAPAGOS_PATH)}/shells/tclScripts/utilities.tcl @@ -132,6 +160,7 @@ if { [info exists ::env(GALAPAGOS_PATH)] } { } +# ADD CLAUSE HERE TO SUPPORT NEW VERSIONS # determine Vivado version set current_vivado_version [version -short] if { [string first 2017.2 $current_vivado_version] != -1 } { @@ -142,6 +171,9 @@ if { [string first 2017.2 $current_vivado_version] != -1 } { set version 2018.1 } elseif { [string first 2018.2 $current_vivado_version] != -1 } { set version 2018.2 +} elseif { [string first 2018.3 $current_vivado_version] != -1 } { + # MM Jan 28/2020: Added support for 2018.3 + set version 2018.3 } else { puts "" catch {common::send_msg_id "BD_TCL-109" "ERROR" "Unsupported Vivado version:\ @@ -1083,9 +1115,9 @@ connect_bd_net [get_bd_pins proc_sys_reset_0/peripheral_aresetn] [get_bd_pins ax # Restore current instance current_bd_instance $oldCurInst - # regenerate_bd_layout - # save_bd_design - # validate_bd_design + regenerate_bd_layout + save_bd_design + validate_bd_design } # End of create_root_design() diff --git a/shells/tclScripts/make_shell.tcl b/shells/tclScripts/make_shell.tcl index 735956ac..2341fdd9 100644 --- a/shells/tclScripts/make_shell.tcl +++ b/shells/tclScripts/make_shell.tcl @@ -126,7 +126,7 @@ if {! [catch {glob $shell_path/srcs/*} yikes] } { } create_bd_design "shell" -# open_bd_design $project_path/$project_name.srcs/sources_1/bd/shell/shell.bd +open_bd_design $project_path/$project_name.srcs/sources_1/bd/shell/shell.bd set ret_val [source $shell_path/tclScripts/shell_bd.tcl] if { $ret_val != 0 } { puts "Error in shell_bd script" diff --git a/shells/tclScripts/shell_procs.tcl b/shells/tclScripts/shell_procs.tcl index 92cf5b35..259b4695 100644 --- a/shells/tclScripts/shell_procs.tcl +++ b/shells/tclScripts/shell_procs.tcl @@ -553,8 +553,12 @@ proc create_hier_eth10G_zu { parentCell nameHier } { connect_bd_net -net util_vector_logic_1_Res [get_bd_pins util_vector_logic_1/Res] [get_bd_pins util_vector_logic_2/Op1] connect_bd_net -net util_vector_logic_2_Res [get_bd_pins ext_reset_n] [get_bd_pins util_vector_logic_2/Res] connect_bd_net -net xlconstant_0_dout [get_bd_pins CONST_5/dout] [get_bd_pins xxv_ethernet_0/rxoutclksel_in_0] [get_bd_pins xxv_ethernet_0/txoutclksel_in_0] - connect_bd_net -net xxv_ethernet_0_rx_clk_out_0 [get_bd_pins axis_clock_converter_0/s_axis_aclk] [get_bd_pins bit_synchronizer_0/sync_clk] [get_bd_pins xxv_ethernet_0/rx_clk_out_0] - connect_bd_net -net xxv_ethernet_0_tx_clk_out_1 [get_bd_pins tx_clk_out_0] [get_bd_pins axis_clock_converter_0/m_axis_aclk] [get_bd_pins axis_register_slice_0/aclk] [get_bd_pins axis_register_slice_1/aclk] [get_bd_pins xxv_ethernet_0/rx_core_clk_0] [get_bd_pins xxv_ethernet_0/tx_clk_out_0] + # + # MM Jan 28/2020: I don't know how this ever worked before, but this uses the wrong clock for rx_core_clk + connect_bd_net -net xxv_ethernet_0_rx_clk_out_0 [get_bd_pins axis_clock_converter_0/s_axis_aclk] [get_bd_pins bit_synchronizer_0/sync_clk] [get_bd_pins xxv_ethernet_0/rx_core_clk_0] [get_bd_pins xxv_ethernet_0/rx_clk_out_0] + connect_bd_net -net xxv_ethernet_0_tx_clk_out_1 [get_bd_pins tx_clk_out_0] [get_bd_pins axis_clock_converter_0/m_axis_aclk] [get_bd_pins axis_register_slice_0/aclk] [get_bd_pins axis_register_slice_1/aclk] [get_bd_pins xxv_ethernet_0/tx_clk_out_0] + + # connect_bd_net -net xxv_ethernet_0_user_rx_reset_0 [get_bd_pins util_vector_logic_1/Op1] [get_bd_pins xxv_ethernet_0/user_rx_reset_0] connect_bd_net -net xxv_ethernet_0_user_tx_reset_0 [get_bd_pins util_vector_logic_1/Op2] [get_bd_pins xxv_ethernet_0/user_tx_reset_0] diff --git a/shells/zedboard/tclScripts/shell_bd.tcl b/shells/zedboard/tclScripts/shell_bd.tcl index 16b2a571..d985e281 100644 --- a/shells/zedboard/tclScripts/shell_bd.tcl +++ b/shells/zedboard/tclScripts/shell_bd.tcl @@ -8,7 +8,7 @@ proc get_script_folder {} { variable script_folder set script_folder [_tcl::get_script_folder] -set supported_versions {2017.4} +set supported_versions {2017.4 2018.3} namespace eval 2017.4 { set ip_list "\ @@ -22,6 +22,18 @@ namespace eval 2017.4 { " } +namespace eval 2018.3 { + set ip_list "\ + xilinx.com:ip:axi_bram_ctrl:4.1\ + xilinx.com:ip:axi_dma:7.1\ + xilinx.com:ip:axi_gpio:2.0\ + xilinx.com:ip:blk_mem_gen:8.4\ + xilinx.com:ip:proc_sys_reset:5.0\ + xilinx.com:ip:processing_system7:5.5\ + xilinx.com:ip:xlconcat:2.1\ + " +} + if { [info exists ::env(GALAPAGOS_PATH)] } { set root_path ${::env(GALAPAGOS_PATH)}/shells } elseif [info exists ::env(SHELLS_PATH)] { @@ -68,6 +80,9 @@ if { $nRet != 0 } { # Check IPs set list_check_ips [puts [subst $\{[subst ${version}::ip_list]\}]] +puts "MARCO SEZ" +puts [subst $\{[subst ${version}::ip_list]\}] +puts "MARCO NO LONGER SEZ" set list_ips_missing "" common::send_msg_id "BD_TCL-006" "INFO" "Checking if the following IPs exist in the project's IP catalog: $list_check_ips ." foreach ip_vlnv $list_check_ips { @@ -190,19 +205,19 @@ proc create_root_design { parentCell } { set sw [ create_bd_port -dir I -from 7 -to 0 sw ] # Create instance: axi_bram_ctrl_0, and set properties - set axi_bram_ctrl_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl:4.0 axi_bram_ctrl_0 ] + set axi_bram_ctrl_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl axi_bram_ctrl_0 ] set_property -dict [ list \ CONFIG.SINGLE_PORT_BRAM {1} \ ] $axi_bram_ctrl_0 # Create instance: axi_bram_ctrl_1, and set properties - set axi_bram_ctrl_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl:4.0 axi_bram_ctrl_1 ] + set axi_bram_ctrl_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl axi_bram_ctrl_1 ] set_property -dict [ list \ CONFIG.SINGLE_PORT_BRAM {1} \ ] $axi_bram_ctrl_1 # Create instance: axi_dma_0, and set properties - set axi_dma_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_dma:7.1 axi_dma_0 ] + set axi_dma_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_dma axi_dma_0 ] set_property -dict [ list \ CONFIG.c_include_mm2s_dre {1} \ CONFIG.c_include_s2mm_dre {1} \ @@ -215,14 +230,14 @@ proc create_root_design { parentCell } { ] $axi_dma_0 # Create instance: axi_gpio_0, and set properties - set axi_gpio_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio:2.0 axi_gpio_0 ] + set axi_gpio_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio axi_gpio_0 ] set_property -dict [ list \ CONFIG.C_ALL_OUTPUTS {1} \ CONFIG.C_GPIO_WIDTH {8} \ ] $axi_gpio_0 # Create instance: axi_gpio_1, and set properties - set axi_gpio_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio:2.0 axi_gpio_1 ] + set axi_gpio_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio axi_gpio_1 ] set_property -dict [ list \ CONFIG.C_ALL_INPUTS {1} \ CONFIG.C_ALL_OUTPUTS {0} \ @@ -231,7 +246,7 @@ proc create_root_design { parentCell } { ] $axi_gpio_1 # Create instance: axi_gpio_2, and set properties - set axi_gpio_2 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio:2.0 axi_gpio_2 ] + set axi_gpio_2 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio axi_gpio_2 ] set_property -dict [ list \ CONFIG.C_ALL_INPUTS {1} \ CONFIG.C_ALL_OUTPUTS {0} \ @@ -240,41 +255,41 @@ proc create_root_design { parentCell } { ] $axi_gpio_2 # Create instance: axi_interconnect_1, and set properties - set axi_interconnect_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_1 ] + set axi_interconnect_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect axi_interconnect_1 ] set_property -dict [ list \ CONFIG.NUM_MI {1} \ CONFIG.NUM_SI {2} \ ] $axi_interconnect_1 # Create instance: axi_interconnect_2, and set properties - set axi_interconnect_2 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_2 ] + set axi_interconnect_2 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect axi_interconnect_2 ] set_property -dict [ list \ CONFIG.NUM_MI {1} \ CONFIG.NUM_SI {1} \ ] $axi_interconnect_2 # Create instance: axi_interconnect_3, and set properties - set axi_interconnect_3 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_3 ] + set axi_interconnect_3 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect axi_interconnect_3 ] set_property -dict [ list \ CONFIG.NUM_MI {2} \ CONFIG.NUM_SI {1} \ ] $axi_interconnect_3 # Create instance: axi_interconnect_4, and set properties - set axi_interconnect_4 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_4 ] + set axi_interconnect_4 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect axi_interconnect_4 ] set_property -dict [ list \ CONFIG.NUM_MI {1} \ CONFIG.NUM_SI {1} \ ] $axi_interconnect_4 # Create instance: axi_interconnect_5, and set properties - set axi_interconnect_5 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 axi_interconnect_5 ] + set axi_interconnect_5 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect axi_interconnect_5 ] set_property -dict [ list \ CONFIG.NUM_MI {4} \ ] $axi_interconnect_5 # Create instance: blk_mem_gen_0, and set properties - set blk_mem_gen_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:blk_mem_gen:8.4 blk_mem_gen_0 ] + set blk_mem_gen_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:blk_mem_gen blk_mem_gen_0 ] set_property -dict [ list \ CONFIG.Enable_B {Use_ENB_Pin} \ CONFIG.Memory_Type {True_Dual_Port_RAM} \ @@ -285,10 +300,10 @@ proc create_root_design { parentCell } { ] $blk_mem_gen_0 # Create instance: proc_sys_reset_0, and set properties - set proc_sys_reset_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:proc_sys_reset:5.0 proc_sys_reset_0 ] + set proc_sys_reset_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:proc_sys_reset proc_sys_reset_0 ] # Create instance: ps7_0, and set properties - set ps7_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 ps7_0 ] + set ps7_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7 ps7_0 ] set_property -dict [ list \ CONFIG.PCW_ACT_APU_PERIPHERAL_FREQMHZ {666.666687} \ CONFIG.PCW_ACT_CAN_PERIPHERAL_FREQMHZ {10.000000} \ @@ -689,7 +704,7 @@ proc create_root_design { parentCell } { ] $ps7_0 # Create instance: xlconcat_1, and set properties - set xlconcat_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat:2.1 xlconcat_1 ] + set xlconcat_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat xlconcat_1 ] set_property -dict [ list \ CONFIG.NUM_PORTS {4} \ ] $xlconcat_1 @@ -731,7 +746,6 @@ proc create_root_design { parentCell } { connect_bd_net -net ps7_0_FCLK_CLK0 [get_bd_ports CLK_DATA] [get_bd_pins axi_bram_ctrl_0/s_axi_aclk] [get_bd_pins axi_bram_ctrl_1/s_axi_aclk] [get_bd_pins axi_dma_0/m_axi_mm2s_aclk] [get_bd_pins axi_dma_0/m_axi_s2mm_aclk] [get_bd_pins axi_dma_0/m_axi_sg_aclk] [get_bd_pins axi_dma_0/s_axi_lite_aclk] [get_bd_pins axi_gpio_0/s_axi_aclk] [get_bd_pins axi_gpio_1/s_axi_aclk] [get_bd_pins axi_gpio_2/s_axi_aclk] [get_bd_pins axi_interconnect_1/ACLK] [get_bd_pins axi_interconnect_1/M00_ACLK] [get_bd_pins axi_interconnect_1/S00_ACLK] [get_bd_pins axi_interconnect_1/S01_ACLK] [get_bd_pins axi_interconnect_2/ACLK] [get_bd_pins axi_interconnect_2/M00_ACLK] [get_bd_pins axi_interconnect_2/S00_ACLK] [get_bd_pins axi_interconnect_3/ACLK] [get_bd_pins axi_interconnect_3/M00_ACLK] [get_bd_pins axi_interconnect_3/M01_ACLK] [get_bd_pins axi_interconnect_3/S00_ACLK] [get_bd_pins axi_interconnect_4/ACLK] [get_bd_pins axi_interconnect_4/M00_ACLK] [get_bd_pins axi_interconnect_4/S00_ACLK] [get_bd_pins axi_interconnect_5/ACLK] [get_bd_pins axi_interconnect_5/M00_ACLK] [get_bd_pins axi_interconnect_5/M01_ACLK] [get_bd_pins axi_interconnect_5/M02_ACLK] [get_bd_pins axi_interconnect_5/M03_ACLK] [get_bd_pins axi_interconnect_5/S00_ACLK] [get_bd_pins proc_sys_reset_0/slowest_sync_clk] [get_bd_pins ps7_0/FCLK_CLK0] [get_bd_pins ps7_0/M_AXI_GP0_ACLK] [get_bd_pins ps7_0/M_AXI_GP1_ACLK] [get_bd_pins ps7_0/S_AXI_HP0_ACLK] [get_bd_pins ps7_0/S_AXI_HP1_ACLK] [get_bd_pins ps7_0/S_AXI_HP2_ACLK] connect_bd_net -net ps7_0_FCLK_RESET0_N [get_bd_ports ARESETN] [get_bd_pins proc_sys_reset_0/ext_reset_in] [get_bd_pins ps7_0/FCLK_RESET0_N] connect_bd_net -net xlconcat_1_dout [get_bd_pins ps7_0/IRQ_F2P] [get_bd_pins xlconcat_1/dout] - connect_bd_net [get_bd_pins xlconcat_1/dout] [get_bd_pins ps7_0/IRQ_F2P # Create address segments create_bd_addr_seg -range 0x00002000 -offset 0x40000000 [get_bd_addr_spaces axi_dma_0/Data_SG] [get_bd_addr_segs axi_bram_ctrl_1/S_AXI/Mem0] SEG_axi_bram_ctrl_1_Mem0