├── .github
└── workflows
│ ├── CI.yml
│ ├── formatter.yml
│ └── linter.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── design
├── wfg_core
│ ├── README.md
│ ├── data
│ │ ├── wfg_core_reg.csv
│ │ └── wfg_core_reg.json
│ ├── formula.png
│ ├── rtl
│ │ ├── wfg_core.sv
│ │ ├── wfg_core_top.sv
│ │ └── wfg_core_wishbone_reg.sv
│ ├── sim
│ │ ├── Makefile
│ │ └── view.gtkw
│ └── testbench
│ │ ├── test_wfg_core.py
│ │ └── wfg_core_tb.sv
├── wfg_drive_pat
│ ├── README.md
│ ├── data
│ │ ├── wfg_drive_pat_reg.csv
│ │ └── wfg_drive_pat_reg.json
│ ├── rtl
│ │ ├── wfg_drive_pat.sv
│ │ ├── wfg_drive_pat_channel.sv
│ │ ├── wfg_drive_pat_top.sv
│ │ └── wfg_drive_pat_wishbone_reg.sv
│ ├── sim
│ │ └── Makefile
│ └── testbench
│ │ ├── test_wfg_drive_pat.py
│ │ └── wfg_drive_pat_tb.sv
├── wfg_drive_spi
│ ├── README.md
│ ├── data
│ │ ├── wfg_drive_spi_reg.csv
│ │ └── wfg_drive_spi_reg.json
│ ├── rtl
│ │ ├── wfg_drive_spi.sv
│ │ ├── wfg_drive_spi_top.sv
│ │ └── wfg_drive_spi_wishbone_reg.sv
│ ├── sim
│ │ ├── Makefile
│ │ └── view.gtkw
│ └── testbench
│ │ ├── test_wfg_drive_spi.py
│ │ └── wfg_drive_spi_tb.sv
├── wfg_interconnect
│ ├── data
│ │ ├── wfg_interconnect_reg.csv
│ │ └── wfg_interconnect_reg.json
│ ├── rtl
│ │ ├── wfg_interconnect.sv
│ │ ├── wfg_interconnect_pkg.svh
│ │ ├── wfg_interconnect_top.sv
│ │ └── wfg_interconnect_wishbone_reg.sv
│ ├── sim
│ │ ├── Makefile
│ │ └── view.gtkw
│ └── testbench
│ │ ├── test_wfg_interconnect.py
│ │ └── wfg_interconnect_tb.sv
├── wfg_stim_mem
│ ├── data
│ │ ├── wfg_stim_mem_reg.csv
│ │ └── wfg_stim_mem_reg.json
│ ├── rtl
│ │ ├── dsp_scale_sn_us.sv
│ │ ├── wfg_stim_mem.sv
│ │ ├── wfg_stim_mem_top.sv
│ │ └── wfg_stim_mem_wishbone_reg.sv
│ ├── sim
│ │ ├── Makefile
│ │ └── memory.hex
│ └── testbench
│ │ ├── test_wfg_stim_mem.py
│ │ └── wfg_stim_mem_tb.sv
├── wfg_stim_sine
│ ├── README.md
│ ├── data
│ │ ├── wfg_stim_sine_reg.csv
│ │ └── wfg_stim_sine_reg.json
│ ├── rtl
│ │ ├── wfg_stim_sine.sv
│ │ ├── wfg_stim_sine_top.sv
│ │ └── wfg_stim_sine_wishbone_reg.sv
│ ├── sim
│ │ └── Makefile
│ └── testbench
│ │ ├── test_wfg_stim_sine.py
│ │ └── wfg_stim_sine_tb.sv
├── wfg_subcore
│ ├── README.md
│ ├── data
│ │ ├── wfg_subcore_reg.csv
│ │ └── wfg_subcore_reg.json
│ ├── formula.png
│ ├── rtl
│ │ ├── wfg_subcore.sv
│ │ ├── wfg_subcore_top.sv
│ │ └── wfg_subcore_wishbone_reg.sv
│ ├── sim
│ │ ├── Makefile
│ │ └── view.gtkw
│ └── testbench
│ │ ├── test_wfg_core.py
│ │ ├── test_wfg_subcore.py
│ │ └── wfg_subcore_tb.sv
└── wfg_top
│ ├── rtl
│ └── wfg_top.sv
│ ├── sim
│ ├── Makefile
│ ├── memory.hex
│ ├── output_inc=1280_gain=16384_off=0.png
│ ├── output_inc=1280_gain=16384_off=0.svg
│ ├── output_inc=4096_gain=16384_off=0.png
│ ├── output_inc=4096_gain=16384_off=0.svg
│ └── view.gtkw
│ └── testbench
│ ├── test_wfg_top.py
│ └── wfg_top_tb.sv
├── fpga
├── ulx3s_barebones
│ ├── ulx3s_top.sv
│ └── ulx3s_v20.lpf
└── ulx3s_soc
│ ├── Makefile
│ ├── analyzer.csv
│ ├── csr.csv
│ ├── femtorv32_quark.v
│ ├── firmware
│ ├── build.make
│ ├── donut.c
│ ├── firmware_build.bin
│ ├── firmware_build.elf
│ ├── firmware_sim.bin
│ ├── firmware_sim.elf
│ ├── linker.ld
│ ├── main.c
│ ├── sim.c
│ ├── sim.make
│ ├── sim_debug.c
│ ├── sim_debug.h
│ └── wfg.c
│ ├── radiona_ulx3s.py
│ └── ulx3s_soc.py
├── img
├── WFG.png
├── litex.png
└── utilization.png
├── requirements.txt
└── templating
├── converter.py
├── generator.py
└── templates
└── wishbone
├── assign_from_registers.template
├── assign_outputs.template
├── assign_to_module.template
├── assign_to_registers.template
├── instantiate_registers.template
├── instantiate_top.template
├── register_interface.template
├── register_overview.template
└── reset_registers.template
/.github/workflows/CI.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 | on: [push]
3 | jobs:
4 | tests:
5 | runs-on: ubuntu-latest
6 | timeout-minutes: 10
7 |
8 | steps:
9 | - uses: actions/checkout@main
10 | - name: Compile and install iverilog
11 | run: |
12 | sudo apt install -y gperf
13 | git clone https://github.com/steveicarus/iverilog.git
14 | cd iverilog
15 | sh autoconf.sh
16 | ./configure
17 | make -j$(nproc)
18 | sudo make install
19 | - name: Install cocotb and modules
20 | run: |
21 | cd ~/work/waveform-generator/waveform-generator/
22 | pip3 install cocotb
23 | pip3 install cocotbext-wishbone
24 | pip3 install cocotbext-axi
25 | pip3 install cocotbext-spi
26 | pip3 install matplotlib
27 | pip3 install scipy
28 | pip3 install numpy
29 | - name: Run tests
30 | run: |
31 | cd ~/work/waveform-generator/waveform-generator/
32 | make templates
33 | make tests
34 | - name: Report failures
35 | run: |
36 | ! grep failure design/*/sim/results.xml
37 |
--------------------------------------------------------------------------------
/.github/workflows/formatter.yml:
--------------------------------------------------------------------------------
1 | name: Verible formatter
2 | on:
3 | pull_request:
4 | jobs:
5 | format:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@master
9 | - uses: chipsalliance/verible-formatter-action@main
10 | with:
11 | parameters:
12 | --indentation_spaces 4
13 | --module_net_variable_alignment=preserve
14 | --case_items_alignment=preserve
15 | github_token: ${{ secrets.GITHUB_TOKEN }}
16 |
--------------------------------------------------------------------------------
/.github/workflows/linter.yml:
--------------------------------------------------------------------------------
1 | name: Verible linter
2 | on: [push]
3 | jobs:
4 | linter:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@master
8 | - uses: chipsalliance/verible-linter-action@main
9 | with:
10 | github_token: ${{ secrets.GITHUB_TOKEN }}
11 | config_file: 'config.rules'
12 | fail_on_error: true
13 | suggest_fixes: 'false'
14 | paths: |
15 | ./design/
16 | extra_args: "--check_syntax=true --rules=-unpacked-dimensions-range-ordering"
17 | - name: Log output
18 | run: cat verible-verilog-lint.log
19 | - name: Upload artifact
20 | uses: actions/upload-artifact@main
21 | with:
22 | name: linter-log
23 | path: verible-verilog-lint.log
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Cache
2 | **/__pycache__
3 | **/sim_build
4 |
5 | # Dump
6 | *.vcd
7 | *.fst
8 |
9 | results.xml
10 | .~lock.*
11 | *.log
12 | *.bit
13 | *.config
14 | ulx3s.json
15 |
16 | build
17 | *.o
18 | *.d
19 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .DEFAULT_GOAL := all
2 |
3 | all: templates
4 |
5 | TEMPLATED_FILES := design/wfg_stim_sine/rtl/wfg_stim_sine_wishbone_reg.sv \
6 | design/wfg_stim_sine/rtl/wfg_stim_sine_top.sv \
7 | design/wfg_stim_mem/rtl/wfg_stim_mem_wishbone_reg.sv \
8 | design/wfg_stim_mem/rtl/wfg_stim_mem_top.sv \
9 | design/wfg_drive_spi/rtl/wfg_drive_spi_top.sv \
10 | design/wfg_drive_spi/rtl/wfg_drive_spi_wishbone_reg.sv \
11 | design/wfg_drive_pat/rtl/wfg_drive_pat_top.sv \
12 | design/wfg_drive_pat/rtl/wfg_drive_pat_wishbone_reg.sv \
13 | design/wfg_interconnect/rtl/wfg_interconnect_top.sv \
14 | design/wfg_interconnect/rtl/wfg_interconnect_wishbone_reg.sv \
15 | design/wfg_core/rtl/wfg_core_top.sv \
16 | design/wfg_core/rtl/wfg_core_wishbone_reg.sv \
17 | design/wfg_subcore/rtl/wfg_subcore_top.sv \
18 | design/wfg_subcore/rtl/wfg_subcore_wishbone_reg.sv
19 |
20 |
21 | DATA_FILES := design/wfg_stim_sine/data/wfg_stim_sine_reg.csv \
22 | design/wfg_stim_mem/data/wfg_stim_mem_reg.csv \
23 | design/wfg_drive_spi/data/wfg_drive_spi_reg.csv \
24 | design/wfg_drive_pat/data/wfg_drive_pat_reg.csv \
25 | design/wfg_interconnect/data/wfg_interconnect_reg.csv \
26 | design/wfg_core/data/wfg_core_reg.csv \
27 | design/wfg_subcore/data/wfg_subcore_reg.csv
28 |
29 | LIBRARIES := $(DATA_FILES:.csv=.json)
30 |
31 | %.json: %.csv
32 | python3 templating/converter.py -i $^ -o $@
33 |
34 | templates: ${TEMPLATED_FILES} ${LIBRARIES}
35 | python3 templating/generator.py --template_dir templating/templates -i ${TEMPLATED_FILES}
36 |
37 | tests:
38 | cd design/wfg_stim_sine/sim; make sim
39 | cd design/wfg_stim_mem/sim; make sim
40 | cd design/wfg_drive_spi/sim; make sim
41 | #cd design/wfg_drive_pat/sim; make sim # TODO fix tests
42 | cd design/wfg_interconnect/sim; make sim
43 | cd design/wfg_core/sim; make sim
44 | cd design/wfg_subcore/sim; make sim
45 | cd design/wfg_top/sim; make sim
46 |
47 | lint:
48 | verible-verilog-lint --rules=-unpacked-dimensions-range-ordering design/*/*/*.sv
49 |
50 | lint-verilator:
51 | verilator --lint-only -Wall design/wfg_drive_spi/rtl/*.sv
52 |
53 | lint-autofix:
54 | verible-verilog-lint --rules=-unpacked-dimensions-range-ordering --autofix inplace-interactive design/*/*/*.sv
55 |
56 | format:
57 | verible-verilog-format --indentation_spaces 4 --module_net_variable_alignment=preserve --case_items_alignment=preserve design/*/*/*.sv --inplace --verbose
58 |
59 | ulx3s.json: design/*/rtl/*.sv
60 | yosys -ql $(basename $@)-yosys.log -p 'synth_ecp5 -top wfg_top -json $@' $^
61 |
62 | nextpnr-view: ulx3s.json
63 | nextpnr-ecp5 --85k --json $< \
64 | --lpf-allow-unconstrained \
65 | --package CABGA381 \
66 | --textcfg ulx3s_out.config --gui
67 |
68 | clean:
69 | rm -rf design/*/sim/sim_build
70 | rm -rf design/*/sim/*.vcd
71 | rm -rf design/*/sim/*.xml
72 | rm -f ulx3s_out.config
73 | rm -f ulx3s-yosys.log
74 | rm -f ulx3s.json
75 |
76 | .PHONY: templates unit-tests lint lint-autofix format clean nextpnr_view
77 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Waveform Generator [](https://github.com/semify-eda/waveform-generator/actions/workflows/CI.yml)
2 |
3 | This repository implements a generic Waveform Generator in SystemVerilog.
4 |
5 | ## Introduction
6 |
7 | The Waveform Generator is split up into three parts:
8 |
9 | - Stimuli
10 | - Interconnect
11 | - Drivers
12 |
13 | 
14 |
15 | Currently the following components are available:
16 |
17 | - `wfg_core`
18 | - `wfg_subcore`
19 | - `wfg_interconnect`
20 | - `wfg_stim_sine`
21 | - `wfg_stim_mem`
22 | - `wfg_drive_spi`
23 | - `wfg_drive_pat`
24 |
25 | ## Prerequisites
26 |
27 | For this project you will need Python3+ and pip.
28 |
29 | For the generation of the register files based on templates install `jinja2`:
30 |
31 | pip3 install jinja2
32 |
33 | The testbench environment for the unit-tests uses `cocotb`. To install it together with the bus interfaces, run:
34 |
35 | pip3 install cocotb
36 | pip3 install cocotbext-axi
37 | pip3 install cocotbext-spi
38 | pip3 install cocotbext-wishbone
39 |
40 | To plot the values during functional verification install the following modules:
41 |
42 | pip3 install scipy
43 | pip3 install numpy
44 | pip3 install matplotlib
45 |
46 | To get more information on assertion fails you can optionally install `pytest`:
47 |
48 | pip3 install pytest
49 |
50 | ## Simulation
51 |
52 | To run the individual unit tests, issue:
53 |
54 | make tests
55 |
56 | ## Template Based Generation
57 |
58 | To generate the register files for the wishbone bus, issue:
59 |
60 | make templates
61 |
62 | ## Code Formatting
63 |
64 | To ensure consistent formatting, [verible](https://github.com/chipsalliance/verible) is used as a SystemVerilog formatter tool.
65 |
66 | make format
67 |
68 | This will format the code according to some custom flags.
69 |
70 | ## Invoke Linter
71 |
72 | To invoke the [verible](https://github.com/chipsalliance/verible) linter, run:
73 |
74 | make lint
75 |
76 | ## FPGA Prototyping with LiteX
77 |
78 | To run the project on an FPGA, [LiteX](https://github.com/enjoy-digital/litex) is used to instantiate the SoC.
79 |
80 | Follow the instructions on the GitHub page to install LiteX. After installation don't forget to add the RISC-V toolchain to the PATH variable.
81 |
82 | To automatically generate documentation about the SoC, install:
83 |
84 | pip3 install sphinx sphinxcontrib-wavedrom
85 |
86 | To trace the simulation using `--trace` you need to install:
87 |
88 | pip3 install pyvcd
89 |
90 | ### Simulation
91 |
92 | To run the simulation with LiteX go to:
93 |
94 | `fpga/ulx3s_soc`
95 |
96 | First run
97 |
98 | make sim-prebuild
99 |
100 | and after that finally:
101 |
102 | make sim
103 |
104 | The output of the simulation window should be:
105 |
106 | 
107 |
108 | ### FPGA Bitstream generation
109 |
110 | To build the bitstream with LiteX for the ULX3S go to:
111 |
112 | `fpga/ulx3s_soc`
113 |
114 | First run
115 |
116 | make build-prebuild
117 |
118 | and after that finally:
119 |
120 | make build
121 |
122 | To upload the bitstream to the ULX3S board, run:
123 |
124 | make only-upload
125 |
126 | ## View FPGA Utilization
127 |
128 | To view the FPGA utilization of only the Waveform Generator with nextpnr, issue the command:
129 |
130 | nextpnr-view
131 |
132 | Next, a window will open. In the top menu, choose the following actions one after another:
133 |
134 | - Pack
135 | - Place
136 | - Route
137 |
138 | After this you can just take a screenshot of the finished design.
139 |
140 | It will look like this:
141 |
142 | 
143 |
144 | ## License
145 |
146 | Copyright [semify](https://www.semify-eda.com/).
147 |
148 | Unless otherwise specified, source code in this repository is licensed under the Apache License Version 2.0 (Apache-2.0). A copy is included in the LICENSE file.
149 |
150 | Other licenses may be specified as well for certain files for purposes of illustration or where third-party components are used.
151 |
--------------------------------------------------------------------------------
/design/wfg_core/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | *Copyright © 2021* [Semify EDA](
4 | https://github.com/semify-eda)
5 |
6 | ## wfg_core documentation
7 | #### Overview
8 | This module is responsible to synchronize all implemented modules, ensuring data transfers with a set frequency.
9 | According to the configuration parameter wfg_pat_subcycle, clock cycles are counted and a subcycle pulse is generated. This subcycle enables finer division of the synchronization pulse and is used for synchronizations with phase shifts. The configuration parameter wfg_pat_sync specifies the number of subcycle pulses during a sync period. The resulting sync frequency
10 |
11 | 
12 |
13 | can be determined using these two parameters. The outputs of the module are subcycle and sync pulses, as well as three optional status signals (active, start pulse, subcyle counter).
--------------------------------------------------------------------------------
/design/wfg_core/data/wfg_core_reg.csv:
--------------------------------------------------------------------------------
1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description
2 | 4'h0,CTRL,,,,,,,Core control register
3 | ,,EN,rw,cfg,0,0,1'b0,Core enable
4 | 4'h4,CFG,,,,,,,Core configuration register
5 | ,,SYNC,rw,cfg,7,0,0,"Sync pulse clock divider.
6 | Count clk until threshold is reached"
7 | ,,SUBCYCLE,rw,cfg,23,8,0,"Subcycle pulse clock divider.
8 | Count clk until threshold is reached"
9 |
--------------------------------------------------------------------------------
/design/wfg_core/data/wfg_core_reg.json:
--------------------------------------------------------------------------------
1 | {
2 | "registers": {
3 | "CFG": {
4 | "address": "4'h4",
5 | "description": "Core configuration register",
6 | "entries": {
7 | "SUBCYCLE": {
8 | "LSB": "8",
9 | "MSB": "23",
10 | "access": "rw",
11 | "description": "Subcycle pulse clock divider.\nCount clk until threshold is reached",
12 | "hardware": "cfg",
13 | "reset": "0"
14 | },
15 | "SYNC": {
16 | "LSB": "0",
17 | "MSB": "7",
18 | "access": "rw",
19 | "description": "Sync pulse clock divider.\nCount clk until threshold is reached",
20 | "hardware": "cfg",
21 | "reset": "0"
22 | }
23 | }
24 | },
25 | "CTRL": {
26 | "address": "4'h0",
27 | "description": "Core control register",
28 | "entries": {
29 | "EN": {
30 | "LSB": "0",
31 | "MSB": "0",
32 | "access": "rw",
33 | "description": "Core enable",
34 | "hardware": "cfg",
35 | "reset": "1'b0"
36 | }
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/design/wfg_core/formula.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/design/wfg_core/formula.png
--------------------------------------------------------------------------------
/design/wfg_core/rtl/wfg_core.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_core (
6 | input wire clk, // I; System clock
7 | input wire rst_n, // I; Active low reset
8 | input wire en_i, // I; Enable signal
9 |
10 | // Config
11 | input wire [ 7:0] wfg_sync_count_i, // I; Sync counter threshold
12 | input wire [15:0] wfg_subcycle_count_i, // I; Subcycle counter threshold
13 |
14 | // Signals
15 | output wire wfg_core_sync_o, // O; Sync signal
16 | output wire wfg_core_subcycle_o, // O; Subcycle signal
17 | output wire wfg_core_start_o, // O; Indicate start
18 | output wire [7:0] wfg_core_subcycle_cnt_o, // O; Subcycle pulse counter
19 | output wire active_o // O; Active indication signal
20 | );
21 |
22 | // -------------------------------------------------------------------------
23 | // Definition
24 | // -------------------------------------------------------------------------
25 |
26 | logic [15:0] subcycle_count; // L; counting variable
27 | logic [ 7:0] sync_count; // L; counting variable
28 | logic temp_subcycle; // L; Internal subcycle
29 | logic temp_sync; // L; Internal subcycle
30 | logic subcycle_dly; // L; Rising edge det subcycle
31 | logic sync_dly; // L; Rising edge det sync
32 | logic en_i_dly; // L; Rising edge det enable
33 | logic [ 7:0] subcycle_pls_cnt; // L; Counts Subcycles
34 |
35 | // -------------------------------------------------------------------------
36 | // Implementation
37 | // -------------------------------------------------------------------------
38 |
39 | always @(posedge clk, negedge rst_n) begin
40 |
41 | if (~rst_n) begin
42 | subcycle_count <= 16'd0;
43 | sync_count <= 8'd0;
44 | temp_subcycle <= 1'b0;
45 | temp_sync <= 1'b0;
46 | subcycle_pls_cnt <= 8'd0;
47 | end else begin
48 |
49 | if (en_i) begin
50 |
51 | if (subcycle_count > 0) begin
52 | temp_subcycle <= temp_subcycle;
53 | subcycle_count <= subcycle_count - 1;
54 | end else begin
55 | temp_subcycle <= ~temp_subcycle;
56 | subcycle_count <= (wfg_subcycle_count_i);
57 |
58 | if (sync_count > 0) begin
59 | temp_sync <= temp_sync;
60 | sync_count <= sync_count - 1;
61 | end else begin
62 | temp_sync <= ~temp_sync;
63 | sync_count <= (wfg_sync_count_i);
64 | end
65 |
66 | end
67 |
68 | if (wfg_core_subcycle_o) begin
69 | subcycle_pls_cnt <= subcycle_pls_cnt + 1;
70 | end
71 |
72 | end else begin
73 | subcycle_count <= 8'd0;
74 | sync_count <= 8'd0;
75 | temp_subcycle <= 1'b0;
76 | temp_sync <= 1'b0;
77 | subcycle_pls_cnt <= 8'd0;
78 | end
79 |
80 | subcycle_dly <= temp_subcycle;
81 | sync_dly <= temp_sync;
82 | en_i_dly <= en_i;
83 |
84 | if (wfg_core_sync_o) begin
85 | subcycle_pls_cnt <= 8'd0;
86 | end
87 |
88 | end
89 |
90 | end
91 |
92 | assign wfg_core_subcycle_o = temp_subcycle & ~subcycle_dly;
93 | assign wfg_core_sync_o = temp_sync & ~sync_dly;
94 | assign active_o = en_i;
95 | assign wfg_core_start_o = en_i & ~en_i_dly;
96 | assign wfg_core_subcycle_cnt_o = subcycle_pls_cnt;
97 |
98 | endmodule
99 | `default_nettype wire
100 |
--------------------------------------------------------------------------------
/design/wfg_core/rtl/wfg_core_top.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_core_top #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output wbs_ack_o,
18 | output [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // Core synchronisation interface
21 | output wire wfg_core_sync_o, // O; Sync signal
22 | output wire wfg_core_subcycle_o, // O; Subcycle signal
23 | output wire wfg_core_start_o, // O; Indicate start
24 | output wire [7:0] wfg_core_subcycle_cnt_o, // O; Subcycle pulse counter
25 | output wire active_o // O; Active indication signal
26 | );
27 | // Registers
28 | //marker_template_start
29 | //data: ../data/wfg_core_reg.json
30 | //template: wishbone/instantiate_top.template
31 | //marker_template_code
32 |
33 | logic [23: 8] cfg_subcycle_q; // CFG.SUBCYCLE register output
34 | logic [ 7: 0] cfg_sync_q; // CFG.SYNC register output
35 | logic ctrl_en_q; // CTRL.EN register output
36 |
37 | //marker_template_end
38 |
39 | wfg_core_wishbone_reg wfg_core_wishbone_reg (
40 | .wb_clk_i (wb_clk_i),
41 | .wb_rst_i (wb_rst_i),
42 | .wbs_stb_i(wbs_stb_i),
43 | .wbs_cyc_i(wbs_cyc_i),
44 | .wbs_we_i (wbs_we_i),
45 | .wbs_sel_i(wbs_sel_i),
46 | .wbs_dat_i(wbs_dat_i),
47 | .wbs_adr_i(wbs_adr_i),
48 | .wbs_ack_o(wbs_ack_o),
49 | .wbs_dat_o(wbs_dat_o),
50 |
51 | //marker_template_start
52 | //data: ../data/wfg_core_reg.json
53 | //template: wishbone/assign_to_module.template
54 | //marker_template_code
55 |
56 | .cfg_subcycle_q_o(cfg_subcycle_q), // CFG.SUBCYCLE register output
57 | .cfg_sync_q_o (cfg_sync_q), // CFG.SYNC register output
58 | .ctrl_en_q_o (ctrl_en_q) // CTRL.EN register output
59 |
60 | //marker_template_end
61 | );
62 |
63 | wfg_core wfg_core (
64 | .clk (wb_clk_i), // clock signal
65 | .rst_n(!wb_rst_i), // reset signal
66 |
67 | // Control
68 | .en_i(ctrl_en_q), // I; Enable signal
69 |
70 | // Configuration
71 | .wfg_sync_count_i (cfg_sync_q), // I; Sync counter threshold
72 | .wfg_subcycle_count_i(cfg_subcycle_q), // I: Subcycle counter threshold
73 |
74 | // Output
75 | .wfg_core_sync_o (wfg_core_sync_o), // O; Sync signal
76 | .wfg_core_subcycle_o (wfg_core_subcycle_o), // O; Subcycle signal
77 | .wfg_core_start_o (wfg_core_start_o), // O; Indicate start
78 | .wfg_core_subcycle_cnt_o(wfg_core_subcycle_cnt_o), // O; Subcycle pulse counter
79 | .active_o (active_o) // O; Active indication signal
80 | );
81 |
82 | endmodule
83 | `default_nettype wire
84 |
--------------------------------------------------------------------------------
/design/wfg_core/rtl/wfg_core_wishbone_reg.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_core_wishbone_reg #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output logic wbs_ack_o,
18 | output logic [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // Registers
21 | //marker_template_start
22 | //data: ../data/wfg_core_reg.json
23 | //template: wishbone/register_interface.template
24 | //marker_template_code
25 |
26 | output logic [23:8] cfg_subcycle_q_o, // CFG.SUBCYCLE register output
27 | output logic [ 7:0] cfg_sync_q_o, // CFG.SYNC register output
28 | output logic ctrl_en_q_o // CTRL.EN register output
29 |
30 | //marker_template_end
31 | );
32 |
33 | //marker_template_start
34 | //data: ../data/wfg_core_reg.json
35 | //template: wishbone/instantiate_registers.template
36 | //marker_template_code
37 |
38 | logic [23: 8] cfg_subcycle_ff; // CFG.SUBCYCLE FF
39 | logic [ 7: 0] cfg_sync_ff; // CFG.SYNC FF
40 | logic ctrl_en_ff; // CTRL.EN FF
41 |
42 | //marker_template_end
43 |
44 | // Wishbone write to slave
45 | always_ff @(posedge wb_clk_i) begin
46 | if (wb_rst_i) begin
47 | //marker_template_start
48 | //data: ../data/wfg_core_reg.json
49 | //template: wishbone/reset_registers.template
50 | //marker_template_code
51 |
52 | cfg_subcycle_ff <= 0;
53 | cfg_sync_ff <= 0;
54 | ctrl_en_ff <= 1'b0;
55 |
56 | //marker_template_end
57 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin
58 | case (wbs_adr_i)
59 | //marker_template_start
60 | //data: ../data/wfg_core_reg.json
61 | //template: wishbone/assign_to_registers.template
62 | //marker_template_code
63 |
64 | 4'h4: begin
65 | cfg_subcycle_ff <= wbs_dat_i[23:8];
66 | cfg_sync_ff <= wbs_dat_i[7:0];
67 | end
68 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0];
69 |
70 | //marker_template_end
71 | default: begin
72 | end
73 | endcase
74 | end
75 | end
76 |
77 | // Wishbone read from slave
78 | always_ff @(posedge wb_clk_i) begin
79 | if (wb_rst_i) begin
80 | wbs_dat_o <= '0;
81 | end else begin
82 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin
83 | wbs_dat_o <= '0; // default value
84 | case (wbs_adr_i)
85 | //marker_template_start
86 | //data: ../data/wfg_core_reg.json
87 | //template: wishbone/assign_from_registers.template
88 | //marker_template_code
89 |
90 | 4'h4: begin
91 | wbs_dat_o[23:8] <= cfg_subcycle_ff;
92 | wbs_dat_o[7:0] <= cfg_sync_ff;
93 | end
94 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff;
95 |
96 | //marker_template_end
97 | default: wbs_dat_o <= 'X;
98 | endcase
99 | end
100 | end
101 | end
102 |
103 | // Acknowledgement
104 | always_ff @(posedge wb_clk_i) begin
105 | if (wb_rst_i) wbs_ack_o <= 1'b0;
106 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o;
107 | end
108 |
109 | //marker_template_start
110 | //data: ../data/wfg_core_reg.json
111 | //template: wishbone/assign_outputs.template
112 | //marker_template_code
113 |
114 | assign cfg_subcycle_q_o = cfg_subcycle_ff;
115 | assign cfg_sync_q_o = cfg_sync_ff;
116 | assign ctrl_en_q_o = ctrl_en_ff;
117 |
118 | //marker_template_end
119 | endmodule
120 | `default_nettype wire
121 |
--------------------------------------------------------------------------------
/design/wfg_core/sim/Makefile:
--------------------------------------------------------------------------------
1 | # source files
2 | SRC := $(wildcard ../rtl/*.sv)
3 | SRC += $(wildcard ../testbench/*.sv)
4 |
5 | # defaults
6 | SIM ?= icarus
7 | TOPLEVEL_LANG ?= verilog
8 |
9 | VERILOG_SOURCES += $(SRC)
10 |
11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
12 | TOPLEVEL = wfg_core_tb
13 |
14 | # MODULE is the basename of the Python test file
15 | export PYTHONPATH := $(PYTHONPATH):../testbench/
16 | MODULE = test_wfg_core
17 |
18 | # include cocotb's make rules to take care of the simulator setup
19 | include $(shell cocotb-config --makefiles)/Makefile.sim
20 |
--------------------------------------------------------------------------------
/design/wfg_core/sim/view.gtkw:
--------------------------------------------------------------------------------
1 | [*]
2 | [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI
3 | [*] Fri May 13 06:43:30 2022
4 | [*]
5 | [dumpfile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_core/sim/wfg_core_tb.vcd"
6 | [dumpfile_mtime] "Fri May 13 06:30:51 2022"
7 | [dumpfile_size] 1052480
8 | [savefile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_core/sim/view.gtkw"
9 | [timestart] 0
10 | [size] 1776 1043
11 | [pos] -1 -1
12 | *-20.000000 810000 -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
13 | [treeopen] wfg_core_tb.
14 | [treeopen] wfg_core_tb.wfg_core_top.
15 | [sst_width] 227
16 | [signals_width] 488
17 | [sst_expanded] 1
18 | [sst_vpaned_height] 410
19 | @200
20 | -Wishbone
21 | @28
22 | wfg_core_tb.io_wbs_clk
23 | @22
24 | wfg_core_tb.io_wbs_adr[31:0]
25 | wfg_core_tb.io_wbs_datrd[31:0]
26 | wfg_core_tb.io_wbs_datwr[31:0]
27 | @28
28 | wfg_core_tb.io_wbs_ack
29 | wfg_core_tb.io_wbs_cyc
30 | wfg_core_tb.io_wbs_rst
31 | wfg_core_tb.io_wbs_stb
32 | wfg_core_tb.io_wbs_we
33 | @200
34 | -Core
35 | @28
36 | wfg_core_tb.wfg_pat_start_o
37 | wfg_core_tb.wfg_pat_subcycle_o
38 | wfg_core_tb.wfg_pat_sync_o
39 | @22
40 | wfg_core_tb.wfg_pat_subcycle_cnt_o[7:0]
41 | @28
42 | wfg_core_tb.active_o
43 | @200
44 | -Internals
45 | @28
46 | wfg_core_tb.wfg_core_top.wfg_core.active_o
47 | wfg_core_tb.wfg_core_top.wfg_core.clk
48 | wfg_core_tb.wfg_core_top.wfg_core.en_i
49 | wfg_core_tb.wfg_core_top.wfg_core.en_i_dly
50 | wfg_core_tb.wfg_core_top.wfg_core.rst_n
51 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_start_o
52 | @22
53 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_count_ff[15:0]
54 | @28
55 | wfg_core_tb.wfg_core_top.wfg_core.temp_subcycle_ff
56 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_dly
57 | @22
58 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_pls_cnt[7:0]
59 | wfg_core_tb.wfg_core_top.wfg_core.sync_count_ff[7:0]
60 | @28
61 | wfg_core_tb.wfg_core_top.wfg_core.temp_sync_ff
62 | wfg_core_tb.wfg_core_top.wfg_core.sync_dly
63 | @22
64 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_subcycle_cnt_o[7:0]
65 | @28
66 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_subcycle_o
67 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_sync_o
68 | @22
69 | wfg_core_tb.wfg_core_top.wfg_core.wfg_subcycle_count_i[15:0]
70 | wfg_core_tb.wfg_core_top.wfg_core.wfg_sync_count_i[7:0]
71 | [pattern_trace] 1
72 | [pattern_trace] 0
73 |
--------------------------------------------------------------------------------
/design/wfg_core/testbench/test_wfg_core.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: © 2022 semify
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | import random
5 | import cocotb
6 | from cocotb.clock import Clock
7 | from cocotb.regression import TestFactory
8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge, ClockCycles
9 | from cocotbext.wishbone.driver import WishboneMaster
10 | from cocotbext.wishbone.driver import WBOp
11 |
12 | CLK_PER_SYNC = 300
13 | SYSCLK = 100000000
14 | DATA_CNT = 10
15 |
16 | short_per = Timer(100, units="ns")
17 | long_time = Timer(100, units="us")
18 |
19 | async def set_register(dut, wbs, address, data):
20 | dut._log.info(f"Set register {address} : {data}")
21 |
22 | wbRes = await wbs.send_cycle([WBOp(address, data)])
23 |
24 | rvalues = [wb.datrd for wb in wbRes]
25 | dut._log.info(f"Returned values : {rvalues}")
26 |
27 | async def configure(dut, wbs, en, sync_count, subcycle_count):
28 | await set_register(dut, wbs, 0x4, (sync_count << 0) | (subcycle_count << 8))
29 | await set_register(dut, wbs, 0x0, en) # Enable core
30 |
31 | @cocotb.coroutine
32 | async def core_test(dut, en, sync_count, subcycle_count):
33 | dut._log.info(f"Configuration: sync_count={sync_count}, subcycle_count={subcycle_count}")
34 |
35 | cocotb.start_soon(Clock(dut.io_wbs_clk, 1/SYSCLK*1e9, units="ns").start())
36 |
37 | dut._log.info("Initialize and reset model")
38 |
39 | # Start reset
40 | dut.io_wbs_rst.value = 1
41 | await Timer(100, units='ns')
42 |
43 | # Stop reset
44 | dut.io_wbs_rst.value = 0
45 |
46 | # Wishbone Master
47 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk,
48 | width=32, # size of data bus
49 | timeout=10) # in clock cycle number
50 |
51 | # Setup core
52 | await configure(dut, wbs, en, sync_count, subcycle_count)
53 |
54 | #await short_per
55 | #await short_per
56 |
57 | sync_pulse_count = 0
58 | clk_count = 0
59 |
60 | await FallingEdge(dut.wfg_core_sync_o)
61 |
62 | for i in range ((sync_count + 1) * (subcycle_count + 1) * 3):
63 | await ClockCycles(dut.io_wbs_clk, 1)
64 | clk_count += 1
65 |
66 | if dut.wfg_core_sync_o == 1:
67 | assert ((sync_pulse_count+1) * clk_count) == ((sync_count + 1) * (subcycle_count + 1) * 2)
68 | break
69 |
70 | if (dut.wfg_core_subcycle_o == 1):
71 | sync_pulse_count += 1
72 | clk_count = 0
73 |
74 | length = 2
75 |
76 | sync_array = []
77 |
78 | for i in range(length):
79 | n = random.randint(1,2**7)
80 | sync_array.append(n)
81 |
82 | subcycle_array = []
83 |
84 | for i in range(length):
85 | n = random.randint(1,2**7)
86 | subcycle_array.append(n)
87 |
88 |
89 | factory = TestFactory(core_test)
90 | factory.add_option("en", [1])
91 | factory.add_option("sync_count", sync_array)
92 | factory.add_option("subcycle_count", subcycle_array)
93 |
94 | factory.generate_tests()
95 |
--------------------------------------------------------------------------------
/design/wfg_core/testbench/wfg_core_tb.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `timescale 1ns / 1ps
5 |
6 | `ifdef VERILATOR // make parameter readable from VPI
7 | `define VL_RD /*verilator public_flat_rd*/
8 | `else
9 | `define VL_RD
10 | `endif
11 |
12 | module wfg_core_tb #(
13 | parameter int BUSW = 32
14 | ) (
15 | // Wishbone interface signals
16 | input io_wbs_clk,
17 | input io_wbs_rst,
18 | input [(BUSW-1):0] io_wbs_adr,
19 | input [(BUSW-1):0] io_wbs_datwr,
20 | output [(BUSW-1):0] io_wbs_datrd,
21 | input io_wbs_we,
22 | input io_wbs_stb,
23 | output io_wbs_ack,
24 | input io_wbs_cyc,
25 |
26 | // Core synchronisation interface
27 | output wire wfg_core_sync_o, // O; Sync signal
28 | output wire wfg_core_subcycle_o, // O; Subcycle signal
29 | output wire wfg_core_start_o, // O; Indicate start
30 | output wire [7:0] wfg_core_subcycle_cnt_o, // O; Subcycle pulse counter
31 | output wire active_o // O; Active indication signal
32 | );
33 |
34 | wfg_core_top wfg_core_top (
35 | .wb_clk_i (io_wbs_clk),
36 | .wb_rst_i (io_wbs_rst),
37 | .wbs_stb_i(io_wbs_stb),
38 | .wbs_cyc_i(io_wbs_cyc),
39 | .wbs_we_i (io_wbs_we),
40 | .wbs_sel_i(4'b1111),
41 | .wbs_dat_i(io_wbs_datwr),
42 | .wbs_adr_i(io_wbs_adr),
43 | .wbs_ack_o(io_wbs_ack),
44 | .wbs_dat_o(io_wbs_datrd),
45 |
46 | // Core synchronisation interface
47 | .wfg_core_sync_o(wfg_core_sync_o),
48 | .wfg_core_subcycle_o(wfg_core_subcycle_o),
49 | .wfg_core_start_o(wfg_core_start_o),
50 | .wfg_core_subcycle_cnt_o(wfg_core_subcycle_cnt_o),
51 | .active_o(active_o)
52 | );
53 |
54 | // Dump waves
55 | `ifndef VERILATOR
56 | initial begin
57 | $dumpfile("wfg_core_tb.vcd");
58 | $dumpvars(0, wfg_core_tb);
59 | end
60 | `endif
61 |
62 | endmodule
63 |
--------------------------------------------------------------------------------
/design/wfg_drive_pat/README.md:
--------------------------------------------------------------------------------
1 | # wfg_drive_pat
2 |
3 | The module wfg_drive_pat drives a pattern onto the number of channels specified by the parameter CHANNELS. CHANNELS can range from 1 to 32.
4 |
5 | For correct operation it needs to be connected to a wfg_core.
6 | The signals used are *pat_sync* and *pat_subcycle_cnt*.
7 |
8 | The input is received via an axi stream (32 bit) interface. One transaction is performed after each *pat_sync*. The lowest CHANNELS bits are used for transmission, higher bits are discarded.
9 |
10 | The fields *begin* and *end* must be set for the channel to work correctly;
11 | begin can be set to a value of `1` to `subcycles - 1`, while end must be set from `begin+1` to `subcycles`, where `subcycles` is to the number of subcycles at which the core puts out *pat_sync* and in the next cycle resets the count to 0.
12 |
13 | Patselect can take values 0-3.
14 | - 0 corresponds to *Return to zero (RZ)*
15 | - 1 corresponds to *Return to one (RO)*
16 | - 2 corresponds to *Non return to one (NRZ)*
17 | - 3 corresponds to *Return to complement (RC)*
18 |
19 | ## Testbench
20 |
21 | *WIP*
22 |
23 | The module wfg_drive_pat is verified using a testbench written in cocotb and python.
24 |
25 | It uses and extends standard components of cocotb.
26 |
27 |
28 | ### Testcase generation
29 |
30 | Testcases are generated with a `TestFactory`.
31 | It takes a list of values for each input port and creates a testcase for each of the combinations.
32 | These values can be fixed to test certain cases, or be set to `None`, in this case they will either be randomized or use a default value.
33 |
34 | Input data for the AXI stream is randomly generated for each testcase run.
35 |
36 |
37 | ### Output checking
38 |
39 | A `OutputMonitor` captures all output values at each clockcycle.
40 | A `Scoreboard` checks the equivalence of the received values to previously generated expected output.
41 |
42 | Expected output is generated at the time the input is randomized. For each clockcycle it is calculated what the corresponding output should be based on the input and the *pat_subcycle_cnt*.
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/design/wfg_drive_pat/data/wfg_drive_pat_reg.csv:
--------------------------------------------------------------------------------
1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description
2 | 4'h0,CTRL,,,,,,,Control register for pattern unit
3 | ,,EN,rw,cfg,31,0,0,Parallel Interface Output enable
4 | 4'h4,CFG,,,,,,,Pattern configuration register
5 | ,,BEGIN,rw,cfg,7,0,0,"Select at which subcycle the output occurs, binary encoded"
6 | ,,END,rw,cfg,15,8,0,"Select at which subcycle the transition according to PATSEL occurs, binary encoded"
7 | ,,CORE_SEL,rw,cfg,16,16,1'b0,"0 : core
8 | 1 : subcore"
9 | 4'h8,PATSEL0,,,,,,,Low bits of PATSEL
10 | ,,LOW,rw,cfg,31,0,0,"Select Output Format
11 |
12 | b00: Return to zero (RZ)
13 | b01: Return to one (RO)
14 | b10: Non return to one (NRZ)
15 | B11: Return to complement (RC)
16 | "
17 | 4'hC,PATSEL1,,,,,,,High bits of PATSEL
18 | ,,HIGH,rw,cfg,31,0,0,"Select Output Format
19 |
20 | b00: Return to zero (RZ)
21 | b01: Return to one (RO)
22 | b10: Non return to one (NRZ)
23 | B11: Return to complement (RC)
24 | "
25 |
--------------------------------------------------------------------------------
/design/wfg_drive_pat/data/wfg_drive_pat_reg.json:
--------------------------------------------------------------------------------
1 | {
2 | "registers": {
3 | "CFG": {
4 | "address": "4'h4",
5 | "description": "Pattern configuration register",
6 | "entries": {
7 | "BEGIN": {
8 | "LSB": "0",
9 | "MSB": "7",
10 | "access": "rw",
11 | "description": "Select at which subcycle the output occurs, binary encoded",
12 | "hardware": "cfg",
13 | "reset": "0"
14 | },
15 | "CORE_SEL": {
16 | "LSB": "16",
17 | "MSB": "16",
18 | "access": "rw",
19 | "description": "0 : core\n1 : subcore",
20 | "hardware": "cfg",
21 | "reset": "1'b0"
22 | },
23 | "END": {
24 | "LSB": "8",
25 | "MSB": "15",
26 | "access": "rw",
27 | "description": "Select at which subcycle the transition according to PATSEL occurs, binary encoded",
28 | "hardware": "cfg",
29 | "reset": "0"
30 | }
31 | }
32 | },
33 | "CTRL": {
34 | "address": "4'h0",
35 | "description": "Control register for pattern unit",
36 | "entries": {
37 | "EN": {
38 | "LSB": "0",
39 | "MSB": "31",
40 | "access": "rw",
41 | "description": "Parallel Interface Output enable",
42 | "hardware": "cfg",
43 | "reset": "0"
44 | }
45 | }
46 | },
47 | "PATSEL0": {
48 | "address": "4'h8",
49 | "description": "Low bits of PATSEL",
50 | "entries": {
51 | "LOW": {
52 | "LSB": "0",
53 | "MSB": "31",
54 | "access": "rw",
55 | "description": "Select Output Format\n \n b00: Return to zero (RZ)\n b01: Return to one (RO)\n b10: Non return to one (NRZ)\n B11: Return to complement (RC)\n",
56 | "hardware": "cfg",
57 | "reset": "0"
58 | }
59 | }
60 | },
61 | "PATSEL1": {
62 | "address": "4'hC",
63 | "description": "High bits of PATSEL",
64 | "entries": {
65 | "HIGH": {
66 | "LSB": "0",
67 | "MSB": "31",
68 | "access": "rw",
69 | "description": "Select Output Format\n \n b00: Return to zero (RZ)\n b01: Return to one (RO)\n b10: Non return to one (NRZ)\n B11: Return to complement (RC)\n",
70 | "hardware": "cfg",
71 | "reset": "0"
72 | }
73 | }
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/design/wfg_drive_pat/rtl/wfg_drive_pat.sv:
--------------------------------------------------------------------------------
1 | // Copyright Message
2 | // --------------------------------------------------------------------------
3 | //
4 | // CONFIDENTIAL and PROPRIETARY
5 | // COPYRIGHT (c) semify 2021
6 | //
7 | // All rights are reserved. Reproduction in whole or in part is
8 | // prohibited without the written consent of the copyright owner.
9 | //
10 | // ----------------------------------------------------------------------------
11 | // Design Information
12 | // ----------------------------------------------------------------------------
13 | //
14 | // Author: Erwin Peterlin
15 | //
16 | // Description : wfg_drive_pat
17 | //
18 |
19 | module wfg_drive_pat #(
20 | parameter int CHANNELS = 8,
21 | parameter int AXIS_WIDTH = 32
22 | ) (
23 | input logic clk, // I; System clock
24 | input logic rst_n, // I; Active low reset
25 |
26 | // Core synchronisation interface
27 | input logic wfg_core_sync_i, // I; Start Pulse for one cycle
28 | input logic [7:0] wfg_core_subcycle_cnt_i, // I; Current Subcycle
29 |
30 | // Subcore synchronisation interface
31 | input logic wfg_subcore_sync_i, // I; Start Pulse for one cycle
32 | input logic [7:0] wfg_subcore_subcycle_cnt_i, // I; Current Subcycle
33 |
34 |
35 | input logic [CHANNELS-1:0] ctrl_en_q_i, // I; Enable signal
36 | input logic [(CHANNELS*2)-1:0] patsel_q_i, // I; Output pattern selector
37 | input logic [7:0] cfg_begin_q_i, // I; Selects at which subcycle the output begins
38 | input logic [7:0] cfg_end_q_i, // I; Selects at which subcycle the output ends
39 | input logic cfg_core_sel_q_i, // I; Core select
40 |
41 | // AXI streaming interface
42 | output wire wfg_axis_tready_o, // O; ready
43 | input wire wfg_axis_tvalid_i, // I; valid
44 | input wire wfg_axis_tlast_i, // I; last
45 | input wire [31:0] wfg_axis_tdata_i, // I; data
46 |
47 | output logic [CHANNELS-1:0] pat_dout_o, // O; output pins
48 | output logic [CHANNELS-1:0] pat_dout_en_o // O; output enabled
49 | );
50 |
51 | // -------------------------------------------------------------------------
52 | // Definition
53 | // -------------------------------------------------------------------------
54 |
55 | assign pat_dout_en_o = ctrl_en_q_i;
56 |
57 | logic [AXIS_WIDTH-1:0] axis_data_ff;
58 | logic axis_ready_ff;
59 | assign wfg_axis_tready_o = axis_ready_ff;
60 |
61 | logic wfg_sync_i;
62 | logic [7:0] wfg_subcycle_cnt_i;
63 |
64 | // -------------------------------------------------------------------------
65 | // Implementation
66 | // -------------------------------------------------------------------------
67 |
68 | always_comb begin
69 | wfg_sync_i = 'x;
70 | wfg_subcycle_cnt_i = 'x;
71 |
72 | case (cfg_core_sel_q_i)
73 | 1'b0: begin
74 | wfg_sync_i = wfg_core_sync_i;
75 | wfg_subcycle_cnt_i = wfg_core_subcycle_cnt_i;
76 | end
77 | 1'b1: begin
78 | wfg_sync_i = wfg_subcore_sync_i;
79 | wfg_subcycle_cnt_i = wfg_subcore_subcycle_cnt_i;
80 | end
81 | endcase
82 | end
83 |
84 | genvar k;
85 | generate
86 | for (k = 0; k < CHANNELS; k++) begin : gen_channels
87 | wfg_drive_pat_channel drv (
88 | .clk(clk),
89 | .rst_n(rst_n),
90 | .wfg_core_subcycle_cnt_i(wfg_subcycle_cnt_i),
91 | .patsel_q_i({patsel_q_i[k+32], patsel_q_i[k]}),
92 | .cfg_begin_q_i(cfg_begin_q_i),
93 | .cfg_end_q_i(cfg_end_q_i),
94 | .ctrl_en_q_i(ctrl_en_q_i[k]),
95 | .axis_data_ff(axis_data_ff[k]),
96 | .data_o(pat_dout_o[k])
97 | );
98 | end
99 | endgenerate
100 |
101 | // -------------------------------------------------------------------------
102 | // ff
103 | // -------------------------------------------------------------------------
104 |
105 | always_ff @(posedge clk or negedge rst_n) begin
106 | if (!rst_n) begin
107 | axis_data_ff <= '0;
108 | axis_ready_ff <= '0;
109 | end else begin
110 |
111 | if (wfg_sync_i) begin
112 | axis_ready_ff <= '1;
113 |
114 | if (wfg_axis_tvalid_i) begin
115 | axis_data_ff <= wfg_axis_tdata_i;
116 | end
117 | end else begin
118 | axis_ready_ff <= '0;
119 | end
120 |
121 | end
122 | end //always_ff
123 | endmodule
124 |
125 |
--------------------------------------------------------------------------------
/design/wfg_drive_pat/rtl/wfg_drive_pat_channel.sv:
--------------------------------------------------------------------------------
1 | // CONFIDENTIAL and PROPRIETARY
2 | // COPYRIGHT (c) semify 2021
3 | //
4 | // All rights are reserved. Reproduction in whole or in part is
5 | // prohibited without the written consent of the copyright owner.
6 | //
7 | // ----------------------------------------------------------------------------
8 | // Design Information
9 | // ----------------------------------------------------------------------------
10 | //
11 | // Author: Erwin Peterlin
12 | //
13 | // Description : wfg_drive_pat_channel
14 | //
15 | module wfg_drive_pat_channel (
16 | input logic clk, // I; System clock
17 | input logic rst_n, // I; Active low reset
18 |
19 | input logic [7:0] wfg_core_subcycle_cnt_i,
20 | input logic [1:0] patsel_q_i,
21 | input logic [7:0] cfg_begin_q_i,
22 | input logic [7:0] cfg_end_q_i,
23 | input logic ctrl_en_q_i,
24 | input logic axis_data_ff,
25 |
26 | output logic data_o
27 | );
28 |
29 | logic data_next, data_ff;
30 | assign data_o = data_ff;
31 |
32 | always_comb begin
33 | data_next = data_ff;
34 |
35 | if (wfg_core_subcycle_cnt_i == cfg_begin_q_i) begin
36 | data_next = axis_data_ff;
37 | end //if
38 |
39 | if (wfg_core_subcycle_cnt_i == cfg_end_q_i) begin
40 | case (patsel_q_i)
41 | 2'b00: begin
42 | data_next = '0;
43 | end //RZ
44 | 2'b01: begin
45 | data_next = '1;
46 | end //RO
47 | 2'b10: begin
48 | data_next = data_ff;
49 | end //NRZ
50 | 2'b11: begin
51 | if (data_ff == '0 || data_ff == '1) begin
52 | data_next = !axis_data_ff;
53 | end //if
54 | end //RC
55 | default: data_next = 'x;
56 | //default: $error("invalid pat_select");
57 | endcase
58 | end //if
59 |
60 | if (!ctrl_en_q_i) begin
61 | data_next = '0;
62 | end //if
63 | end //always_comb
64 |
65 |
66 | always_ff @(posedge clk or negedge rst_n) begin
67 | if (!rst_n) begin
68 | data_ff <= '0;
69 | end else begin
70 | data_ff <= data_next;
71 | end
72 | end //always_ff
73 | endmodule
74 |
--------------------------------------------------------------------------------
/design/wfg_drive_pat/rtl/wfg_drive_pat_top.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_drive_pat_top #(
6 | parameter int BUSW = 32,
7 | parameter int AXIS_DATA_WIDTH = 32,
8 | parameter int CHANNELS = 32
9 | ) (
10 | // Wishbone Slave ports
11 | input wb_clk_i,
12 | input wb_rst_i,
13 | input wbs_stb_i,
14 | input wbs_cyc_i,
15 | input wbs_we_i,
16 | input [(BUSW/8-1):0] wbs_sel_i,
17 | input [ (BUSW-1):0] wbs_dat_i,
18 | input [ (BUSW-1):0] wbs_adr_i,
19 | output wbs_ack_o,
20 | output [ (BUSW-1):0] wbs_dat_o,
21 |
22 | // Core synchronisation interface
23 | input wire wfg_core_sync_i, // I; sync pulse
24 | input logic [7:0] wfg_core_subcycle_cnt_i, // I; subcycle_cnt
25 |
26 | // Subcore synchronisation interface
27 | input logic wfg_subcore_sync_i, // I; sync pulse
28 | input logic [7:0] wfg_subcore_subcycle_cnt_i, // I; subcycle_cnt
29 |
30 | // AXI-Stream interface
31 | output wire wfg_axis_tready_o,
32 | input wire [AXIS_DATA_WIDTH-1:0] wfg_axis_tdata_i,
33 | input wire wfg_axis_tlast_i,
34 | input wire wfg_axis_tvalid_i,
35 |
36 | output logic [CHANNELS-1:0] pat_dout_o, // O; output pins
37 | output logic [CHANNELS-1:0] pat_dout_en_o // O; output enabled
38 | );
39 | // Registers
40 | //marker_template_start
41 | //data: ../data/wfg_drive_pat_reg.json
42 | //template: wishbone/instantiate_top.template
43 | //marker_template_code
44 |
45 | logic [ 7: 0] cfg_begin_q; // CFG.BEGIN register output
46 | logic cfg_core_sel_q; // CFG.CORE_SEL register output
47 | logic [15: 8] cfg_end_q; // CFG.END register output
48 | logic [31: 0] ctrl_en_q; // CTRL.EN register output
49 | logic [31: 0] patsel0_low_q; // PATSEL0.LOW register output
50 | logic [31: 0] patsel1_high_q; // PATSEL1.HIGH register output
51 |
52 | //marker_template_end
53 |
54 | wfg_drive_pat_wishbone_reg wfg_drive_pat_wishbone_reg (
55 | .wb_clk_i (wb_clk_i),
56 | .wb_rst_i (wb_rst_i),
57 | .wbs_stb_i(wbs_stb_i),
58 | .wbs_cyc_i(wbs_cyc_i),
59 | .wbs_we_i (wbs_we_i),
60 | .wbs_sel_i(wbs_sel_i),
61 | .wbs_dat_i(wbs_dat_i),
62 | .wbs_adr_i(wbs_adr_i),
63 | .wbs_ack_o(wbs_ack_o),
64 | .wbs_dat_o(wbs_dat_o),
65 |
66 | //marker_template_start
67 | //data: ../data/wfg_drive_pat_reg.json
68 | //template: wishbone/assign_to_module.template
69 | //marker_template_code
70 |
71 | .cfg_begin_q_o (cfg_begin_q), // CFG.BEGIN register output
72 | .cfg_core_sel_q_o(cfg_core_sel_q), // CFG.CORE_SEL register output
73 | .cfg_end_q_o (cfg_end_q), // CFG.END register output
74 | .ctrl_en_q_o (ctrl_en_q), // CTRL.EN register output
75 | .patsel0_low_q_o (patsel0_low_q), // PATSEL0.LOW register output
76 | .patsel1_high_q_o(patsel1_high_q) // PATSEL1.HIGH register output
77 |
78 | //marker_template_end
79 | );
80 |
81 | wfg_drive_pat #(
82 | .CHANNELS (CHANNELS),
83 | .AXIS_WIDTH(AXIS_DATA_WIDTH)
84 | ) wfg_drive_pat (
85 | .clk (wb_clk_i), // clock signal
86 | .rst_n(!wb_rst_i), // reset signal
87 |
88 | // Core synchronisation interface
89 | .wfg_core_sync_i (wfg_core_sync_i),
90 | .wfg_core_subcycle_cnt_i(wfg_core_subcycle_cnt_i),
91 |
92 | // Subcore synchronisation interface
93 | .wfg_subcore_sync_i (wfg_subcore_sync_i),
94 | .wfg_subcore_subcycle_cnt_i(wfg_subcore_subcycle_cnt_i),
95 |
96 | // AXI streaming interface
97 | .wfg_axis_tready_o(wfg_axis_tready_o), // O; ready
98 | .wfg_axis_tvalid_i(wfg_axis_tvalid_i), // I; valid
99 | .wfg_axis_tlast_i (wfg_axis_tlast_i), // I; last
100 | .wfg_axis_tdata_i (wfg_axis_tdata_i), // I; data
101 |
102 | // Control
103 | .ctrl_en_q_i(ctrl_en_q), // I; pat enable
104 |
105 | // Configuration
106 | .cfg_begin_q_i (cfg_begin_q),
107 | .cfg_end_q_i (cfg_end_q),
108 | .patsel_q_i ({patsel1_high_q, patsel0_low_q}),
109 | .cfg_core_sel_q_i(cfg_core_sel_q),
110 |
111 | // Output
112 | .pat_dout_o (pat_dout_o),
113 | .pat_dout_en_o(pat_dout_en_o)
114 | );
115 |
116 | endmodule
117 | `default_nettype wire
118 |
--------------------------------------------------------------------------------
/design/wfg_drive_pat/rtl/wfg_drive_pat_wishbone_reg.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_drive_pat_wishbone_reg #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output logic wbs_ack_o,
18 | output logic [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // Registers
21 | //marker_template_start
22 | //data: ../data/wfg_drive_pat_reg.json
23 | //template: wishbone/register_interface.template
24 | //marker_template_code
25 |
26 | output logic [ 7:0] cfg_begin_q_o, // CFG.BEGIN register output
27 | output logic cfg_core_sel_q_o, // CFG.CORE_SEL register output
28 | output logic [15:8] cfg_end_q_o, // CFG.END register output
29 | output logic [31:0] ctrl_en_q_o, // CTRL.EN register output
30 | output logic [31:0] patsel0_low_q_o, // PATSEL0.LOW register output
31 | output logic [31:0] patsel1_high_q_o // PATSEL1.HIGH register output
32 |
33 | //marker_template_end
34 | );
35 |
36 | //marker_template_start
37 | //data: ../data/wfg_drive_pat_reg.json
38 | //template: wishbone/instantiate_registers.template
39 | //marker_template_code
40 |
41 | logic [ 7: 0] cfg_begin_ff; // CFG.BEGIN FF
42 | logic cfg_core_sel_ff; // CFG.CORE_SEL FF
43 | logic [15: 8] cfg_end_ff; // CFG.END FF
44 | logic [31: 0] ctrl_en_ff; // CTRL.EN FF
45 | logic [31: 0] patsel0_low_ff; // PATSEL0.LOW FF
46 | logic [31: 0] patsel1_high_ff; // PATSEL1.HIGH FF
47 |
48 | //marker_template_end
49 |
50 | // Wishbone write to slave
51 | always_ff @(posedge wb_clk_i) begin
52 | if (wb_rst_i) begin
53 | //marker_template_start
54 | //data: ../data/wfg_drive_pat_reg.json
55 | //template: wishbone/reset_registers.template
56 | //marker_template_code
57 |
58 | cfg_begin_ff <= 0;
59 | cfg_core_sel_ff <= 1'b0;
60 | cfg_end_ff <= 0;
61 | ctrl_en_ff <= 0;
62 | patsel0_low_ff <= 0;
63 | patsel1_high_ff <= 0;
64 |
65 | //marker_template_end
66 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin
67 | case (wbs_adr_i)
68 | //marker_template_start
69 | //data: ../data/wfg_drive_pat_reg.json
70 | //template: wishbone/assign_to_registers.template
71 | //marker_template_code
72 |
73 | 4'h4: begin
74 | cfg_begin_ff <= wbs_dat_i[7:0];
75 | cfg_core_sel_ff <= wbs_dat_i[16:16];
76 | cfg_end_ff <= wbs_dat_i[15:8];
77 | end
78 | 4'h0: ctrl_en_ff <= wbs_dat_i[31: 0];
79 | 4'h8: patsel0_low_ff <= wbs_dat_i[31: 0];
80 | 4'hC: patsel1_high_ff <= wbs_dat_i[31: 0];
81 |
82 | //marker_template_end
83 | default: begin
84 | end
85 | endcase
86 | end
87 | end
88 |
89 | // Wishbone read from slave
90 | always_ff @(posedge wb_clk_i) begin
91 | if (wb_rst_i) begin
92 | wbs_dat_o <= '0;
93 | end else begin
94 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin
95 | wbs_dat_o <= '0; // default value
96 | case (wbs_adr_i)
97 | //marker_template_start
98 | //data: ../data/wfg_drive_pat_reg.json
99 | //template: wishbone/assign_from_registers.template
100 | //marker_template_code
101 |
102 | 4'h4: begin
103 | wbs_dat_o[7:0] <= cfg_begin_ff;
104 | wbs_dat_o[16:16] <= cfg_core_sel_ff;
105 | wbs_dat_o[15:8] <= cfg_end_ff;
106 | end
107 | 4'h0: wbs_dat_o[31: 0] <= ctrl_en_ff;
108 | 4'h8: wbs_dat_o[31: 0] <= patsel0_low_ff;
109 | 4'hC: wbs_dat_o[31: 0] <= patsel1_high_ff;
110 |
111 | //marker_template_end
112 | default: wbs_dat_o <= 'X;
113 | endcase
114 | end
115 | end
116 | end
117 |
118 | // Acknowledgement
119 | always_ff @(posedge wb_clk_i) begin
120 | if (wb_rst_i) wbs_ack_o <= 1'b0;
121 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o;
122 | end
123 |
124 | //marker_template_start
125 | //data: ../data/wfg_drive_pat_reg.json
126 | //template: wishbone/assign_outputs.template
127 | //marker_template_code
128 |
129 | assign cfg_begin_q_o = cfg_begin_ff;
130 | assign cfg_core_sel_q_o = cfg_core_sel_ff;
131 | assign cfg_end_q_o = cfg_end_ff;
132 | assign ctrl_en_q_o = ctrl_en_ff;
133 | assign patsel0_low_q_o = patsel0_low_ff;
134 | assign patsel1_high_q_o = patsel1_high_ff;
135 |
136 | //marker_template_end
137 | endmodule
138 | `default_nettype wire
139 |
--------------------------------------------------------------------------------
/design/wfg_drive_pat/sim/Makefile:
--------------------------------------------------------------------------------
1 | # source files
2 | SRC := $(wildcard ../rtl/*.sv)
3 | SRC += $(wildcard ../testbench/*.sv)
4 |
5 | # defaults
6 | SIM ?= icarus
7 | TOPLEVEL_LANG ?= verilog
8 |
9 | VERILOG_SOURCES += $(SRC)
10 |
11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
12 | TOPLEVEL = wfg_drive_pat_tb
13 |
14 | # MODULE is the basename of the Python test file
15 | export PYTHONPATH := $(PYTHONPATH):../testbench/
16 | MODULE = test_wfg_drive_pat
17 |
18 | # include cocotb's make rules to take care of the simulator setup
19 | include $(shell cocotb-config --makefiles)/Makefile.sim
20 |
--------------------------------------------------------------------------------
/design/wfg_drive_pat/testbench/wfg_drive_pat_tb.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `timescale 1ns / 1ps
5 |
6 | `ifdef VERILATOR // make parameter readable from VPI
7 | `define VL_RD /*verilator public_flat_rd*/
8 | `else
9 | `define VL_RD
10 | `endif
11 |
12 | module wfg_drive_pat_tb #(
13 | parameter int BUSW = 32,
14 | parameter int AXIS_DATA_WIDTH = 32,
15 | parameter int CHANNELS = 32
16 | ) (
17 | // Wishbone interface signals
18 | input io_wbs_clk,
19 | input io_wbs_rst,
20 | input [(BUSW-1):0] io_wbs_adr,
21 | input [(BUSW-1):0] io_wbs_datwr,
22 | output [(BUSW-1):0] io_wbs_datrd,
23 | input io_wbs_we,
24 | input io_wbs_stb,
25 | output io_wbs_ack,
26 | input io_wbs_cyc,
27 |
28 | // Core synchronisation interface
29 | input logic wfg_core_sync_i,
30 | input logic [7:0] wfg_core_subcycle_cnt_i,
31 |
32 | // AXI-Stream interface
33 | output wire wfg_axis_tready, // O; ready
34 | input logic wfg_axis_tvalid, // I; valid
35 | input logic wfg_axis_tlast, // I; last
36 | input logic [AXIS_DATA_WIDTH-1:0] wfg_axis_tdata, // I; data
37 |
38 | // pat IO interface
39 | output wire [CHANNELS-1:0] wfg_drive_pat_dout_o, // O; output pins
40 | output wire [CHANNELS-1:0] wfg_drive_pat_dout_en_o // O; output enabled
41 | );
42 |
43 | wfg_drive_pat_top #(
44 | .BUSW(BUSW),
45 | .CHANNELS(CHANNELS),
46 | .AXIS_DATA_WIDTH(AXIS_DATA_WIDTH)
47 | ) wfg_drive_pat_top (
48 | .wb_clk_i (io_wbs_clk),
49 | .wb_rst_i (io_wbs_rst),
50 | .wbs_stb_i(io_wbs_stb),
51 | .wbs_cyc_i(io_wbs_cyc),
52 | .wbs_we_i (io_wbs_we),
53 | .wbs_sel_i(4'b1111),
54 | .wbs_dat_i(io_wbs_datwr),
55 | .wbs_adr_i(io_wbs_adr),
56 | .wbs_ack_o(io_wbs_ack),
57 | .wbs_dat_o(io_wbs_datrd),
58 |
59 | .wfg_core_sync_i(wfg_core_sync_i),
60 | .wfg_core_subcycle_cnt_i(wfg_core_subcycle_cnt_i),
61 |
62 | .wfg_axis_tready_o(wfg_axis_tready),
63 | .wfg_axis_tdata_i (wfg_axis_tdata),
64 | .wfg_axis_tlast_i (wfg_axis_tlast),
65 | .wfg_axis_tvalid_i(wfg_axis_tvalid),
66 |
67 | .pat_dout_o(wfg_drive_pat_dout_o),
68 | .pat_dout_en_o(wfg_drive_pat_dout_en_o)
69 | );
70 |
71 | // Dump waves
72 | `ifndef VERILATOR
73 | initial begin
74 | $dumpfile("wfg_drive_pat_tb.vcd");
75 | $dumpvars(0, wfg_drive_pat_tb);
76 | end
77 | `endif
78 |
79 | endmodule
80 |
--------------------------------------------------------------------------------
/design/wfg_drive_spi/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | *Copyright © 2021* [Semify EDA](
4 | https://github.com/semify-eda)
5 |
6 | ## wfg_drive_spi documentation
7 |
8 | #### Overview
9 |
10 | The wfg_drive_spi module is a SPI master module used in the output stage of the waveform generator and is only operating in MOSI mode. The module is
11 | triggered by the wfg_core, each sync puls starts a new transmission.
12 | The input is received via an axi stream (32 bit) interface.
13 | The transmission is handled entirely within the module and all SPI signals are generated automatically and are configurable.
14 |
15 | #### Verification
16 |
17 | A system clock with frequency f = 100 MHz and an asynchronous wfg_sync signal are defined in the cocotb testbench.
18 | The Python function random.randint is used to generate a list of random input values. Via a cocotb AXI-Stream master the SPI module receives a new input
19 | at each sync pulse. The functionality is tested with a test factory that runs through all possible configuration parameters.
20 | The SPI module is connected to a cocotb SPI interface which decodes each successful transmission.
21 |
--------------------------------------------------------------------------------
/design/wfg_drive_spi/data/wfg_drive_spi_reg.csv:
--------------------------------------------------------------------------------
1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description
2 | 4'h0,CTRL,,,,,,,Control register for SPI unit
3 | ,,EN,rw,cfg,0,0,1'b0,SPI enable
4 | 4'h4,CFG,,,,,,,SPI configuration register
5 | ,,CPOL,rw,cfg,0,0,1'b0,"Clock polarity
6 | 0: CK to 0 when idle
7 | 1: CK to 1 when idle"
8 | ,,LSBFIRST,rw,cfg,1,1,1'b0,"Frame format
9 | 0: MSB transmitted first
10 | 1: LSB transmitted first"
11 | ,,DFF,rw,cfg,3,2,2'b00,"Data frame format
12 | 00: 8bit
13 | 01: 16bit
14 | 10: 24bit
15 | 11: 32bit"
16 | ,,SSPOL,rw,cfg,4,4,1'b0,"Slave select polarity
17 | 0: Active low
18 | 1: Active high"
19 | ,,CORE_SEL,rw,cfg,5,5,1'b0,"0 : core
20 | 1 : subcore"
21 | 4'h8,CLKCFG,,,,,,,SPI clock configuration register
22 | ,,DIV,rw,cfg,7,0,0,"SPI Clock divider.
23 | Divider ratio is 2*DIV+1"
24 |
--------------------------------------------------------------------------------
/design/wfg_drive_spi/data/wfg_drive_spi_reg.json:
--------------------------------------------------------------------------------
1 | {
2 | "registers": {
3 | "CFG": {
4 | "address": "4'h4",
5 | "description": "SPI configuration register",
6 | "entries": {
7 | "CORE_SEL": {
8 | "LSB": "5",
9 | "MSB": "5",
10 | "access": "rw",
11 | "description": "0 : core\n1 : subcore",
12 | "hardware": "cfg",
13 | "reset": "1'b0"
14 | },
15 | "CPOL": {
16 | "LSB": "0",
17 | "MSB": "0",
18 | "access": "rw",
19 | "description": "Clock polarity\n 0: CK to 0 when idle\n 1: CK to 1 when idle",
20 | "hardware": "cfg",
21 | "reset": "1'b0"
22 | },
23 | "DFF": {
24 | "LSB": "2",
25 | "MSB": "3",
26 | "access": "rw",
27 | "description": "Data frame format\n 00: 8bit\n 01: 16bit\n 10: 24bit\n 11: 32bit",
28 | "hardware": "cfg",
29 | "reset": "2'b00"
30 | },
31 | "LSBFIRST": {
32 | "LSB": "1",
33 | "MSB": "1",
34 | "access": "rw",
35 | "description": "Frame format\n 0: MSB transmitted first\n 1: LSB transmitted first",
36 | "hardware": "cfg",
37 | "reset": "1'b0"
38 | },
39 | "SSPOL": {
40 | "LSB": "4",
41 | "MSB": "4",
42 | "access": "rw",
43 | "description": "Slave select polarity\n 0: Active low\n 1: Active high",
44 | "hardware": "cfg",
45 | "reset": "1'b0"
46 | }
47 | }
48 | },
49 | "CLKCFG": {
50 | "address": "4'h8",
51 | "description": "SPI clock configuration register",
52 | "entries": {
53 | "DIV": {
54 | "LSB": "0",
55 | "MSB": "7",
56 | "access": "rw",
57 | "description": "SPI Clock divider.\nDivider ratio is 2*DIV+1",
58 | "hardware": "cfg",
59 | "reset": "0"
60 | }
61 | }
62 | },
63 | "CTRL": {
64 | "address": "4'h0",
65 | "description": "Control register for SPI unit",
66 | "entries": {
67 | "EN": {
68 | "LSB": "0",
69 | "MSB": "0",
70 | "access": "rw",
71 | "description": "SPI enable",
72 | "hardware": "cfg",
73 | "reset": "1'b0"
74 | }
75 | }
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/design/wfg_drive_spi/rtl/wfg_drive_spi_top.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_drive_spi_top #(
6 | parameter int BUSW = 32,
7 | parameter int AXIS_DATA_WIDTH = 32
8 | ) (
9 | // Wishbone Slave ports
10 | input wb_clk_i,
11 | input wb_rst_i,
12 | input wbs_stb_i,
13 | input wbs_cyc_i,
14 | input wbs_we_i,
15 | input [(BUSW/8-1):0] wbs_sel_i,
16 | input [ (BUSW-1):0] wbs_dat_i,
17 | input [ (BUSW-1):0] wbs_adr_i,
18 | output wbs_ack_o,
19 | output [ (BUSW-1):0] wbs_dat_o,
20 |
21 | // Core synchronisation interface
22 | input wire wfg_core_sync_i, // I; core_sync pulse
23 | input wire wfg_core_subcycle_i, // I; subcycle_cnt
24 |
25 | // Subcore synchronisation interface
26 | input logic wfg_subcore_sync_i, // I; Sync pulse
27 | input logic wfg_subcore_subcycle_i, // I; Subcycle pulse
28 |
29 | // AXI-Stream interface
30 | output wire wfg_axis_tready_o,
31 | input wire [AXIS_DATA_WIDTH-1:0] wfg_axis_tdata_i,
32 | input wire wfg_axis_tlast_i,
33 | input wire wfg_axis_tvalid_i,
34 |
35 | // SPI IO interface
36 | output wire wfg_drive_spi_sclk_o, // O; clock
37 | output wire wfg_drive_spi_cs_no, // O; chip select
38 | output wire wfg_drive_spi_sdo_o // O; data out
39 | );
40 | // Registers
41 | //marker_template_start
42 | //data: ../data/wfg_drive_spi_reg.json
43 | //template: wishbone/instantiate_top.template
44 | //marker_template_code
45 |
46 | logic cfg_core_sel_q; // CFG.CORE_SEL register output
47 | logic cfg_cpol_q; // CFG.CPOL register output
48 | logic [ 3: 2] cfg_dff_q; // CFG.DFF register output
49 | logic cfg_lsbfirst_q; // CFG.LSBFIRST register output
50 | logic cfg_sspol_q; // CFG.SSPOL register output
51 | logic [ 7: 0] clkcfg_div_q; // CLKCFG.DIV register output
52 | logic ctrl_en_q; // CTRL.EN register output
53 |
54 | //marker_template_end
55 |
56 | wfg_drive_spi_wishbone_reg wfg_drive_spi_wishbone_reg (
57 | .wb_clk_i (wb_clk_i),
58 | .wb_rst_i (wb_rst_i),
59 | .wbs_stb_i(wbs_stb_i),
60 | .wbs_cyc_i(wbs_cyc_i),
61 | .wbs_we_i (wbs_we_i),
62 | .wbs_sel_i(wbs_sel_i),
63 | .wbs_dat_i(wbs_dat_i),
64 | .wbs_adr_i(wbs_adr_i),
65 | .wbs_ack_o(wbs_ack_o),
66 | .wbs_dat_o(wbs_dat_o),
67 |
68 | //marker_template_start
69 | //data: ../data/wfg_drive_spi_reg.json
70 | //template: wishbone/assign_to_module.template
71 | //marker_template_code
72 |
73 | .cfg_core_sel_q_o(cfg_core_sel_q), // CFG.CORE_SEL register output
74 | .cfg_cpol_q_o (cfg_cpol_q), // CFG.CPOL register output
75 | .cfg_dff_q_o (cfg_dff_q), // CFG.DFF register output
76 | .cfg_lsbfirst_q_o(cfg_lsbfirst_q), // CFG.LSBFIRST register output
77 | .cfg_sspol_q_o (cfg_sspol_q), // CFG.SSPOL register output
78 | .clkcfg_div_q_o (clkcfg_div_q), // CLKCFG.DIV register output
79 | .ctrl_en_q_o (ctrl_en_q) // CTRL.EN register output
80 |
81 | //marker_template_end
82 | );
83 |
84 | wfg_drive_spi wfg_drive_spi (
85 | .clk (wb_clk_i), // clock signal
86 | .rst_n(!wb_rst_i), // reset signal
87 |
88 | // Core synchronisation interface
89 | .wfg_core_sync_i (wfg_core_sync_i),
90 | .wfg_core_subcycle_i(wfg_core_subcycle_i),
91 |
92 | // Subcore synchronisation interface
93 | .wfg_subcore_sync_i (wfg_subcore_sync_i),
94 | .wfg_subcore_subcycle_i(wfg_subcore_subcycle_i),
95 |
96 | // AXI streaming interface
97 | .wfg_axis_tready_o(wfg_axis_tready_o), // O; ready
98 | .wfg_axis_tvalid_i(wfg_axis_tvalid_i), // I; valid
99 | .wfg_axis_tlast_i (wfg_axis_tlast_i), // I; last
100 | .wfg_axis_tdata_i (wfg_axis_tdata_i), // I; data
101 |
102 | // Control
103 | .ctrl_en_q_i(ctrl_en_q), // I; SPI enable
104 |
105 | // Configuration
106 | .clkcfg_div_q_i (clkcfg_div_q), // I: clock divider
107 | .cfg_cpol_q_i (cfg_cpol_q), // I; Clock polarity
108 | .cfg_lsbfirst_q_i(cfg_lsbfirst_q), // I; Frame format
109 | .cfg_dff_q_i (cfg_dff_q), // I; Data frame format
110 | .cfg_sspol_q_i (cfg_sspol_q), // I; Slave select polarity
111 | .cfg_core_sel_q_i(cfg_core_sel_q), // I; Core select
112 |
113 | // SPI IO interface
114 | .wfg_drive_spi_sclk_o(wfg_drive_spi_sclk_o), // O; clock
115 | .wfg_drive_spi_cs_no (wfg_drive_spi_cs_no), // O; chip select
116 | .wfg_drive_spi_sdo_o (wfg_drive_spi_sdo_o) // O; data out
117 | );
118 |
119 | endmodule
120 | `default_nettype wire
121 |
--------------------------------------------------------------------------------
/design/wfg_drive_spi/rtl/wfg_drive_spi_wishbone_reg.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_drive_spi_wishbone_reg #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output logic wbs_ack_o,
18 | output logic [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // Registers
21 | //marker_template_start
22 | //data: ../data/wfg_drive_spi_reg.json
23 | //template: wishbone/register_interface.template
24 | //marker_template_code
25 |
26 | output logic cfg_core_sel_q_o, // CFG.CORE_SEL register output
27 | output logic cfg_cpol_q_o, // CFG.CPOL register output
28 | output logic [3:2] cfg_dff_q_o, // CFG.DFF register output
29 | output logic cfg_lsbfirst_q_o, // CFG.LSBFIRST register output
30 | output logic cfg_sspol_q_o, // CFG.SSPOL register output
31 | output logic [7:0] clkcfg_div_q_o, // CLKCFG.DIV register output
32 | output logic ctrl_en_q_o // CTRL.EN register output
33 |
34 | //marker_template_end
35 | );
36 |
37 | //marker_template_start
38 | //data: ../data/wfg_drive_spi_reg.json
39 | //template: wishbone/instantiate_registers.template
40 | //marker_template_code
41 |
42 | logic cfg_core_sel_ff; // CFG.CORE_SEL FF
43 | logic cfg_cpol_ff; // CFG.CPOL FF
44 | logic [ 3: 2] cfg_dff_ff; // CFG.DFF FF
45 | logic cfg_lsbfirst_ff; // CFG.LSBFIRST FF
46 | logic cfg_sspol_ff; // CFG.SSPOL FF
47 | logic [ 7: 0] clkcfg_div_ff; // CLKCFG.DIV FF
48 | logic ctrl_en_ff; // CTRL.EN FF
49 |
50 | //marker_template_end
51 |
52 | // Wishbone write to slave
53 | always_ff @(posedge wb_clk_i) begin
54 | if (wb_rst_i) begin
55 | //marker_template_start
56 | //data: ../data/wfg_drive_spi_reg.json
57 | //template: wishbone/reset_registers.template
58 | //marker_template_code
59 |
60 | cfg_core_sel_ff <= 1'b0;
61 | cfg_cpol_ff <= 1'b0;
62 | cfg_dff_ff <= 2'b00;
63 | cfg_lsbfirst_ff <= 1'b0;
64 | cfg_sspol_ff <= 1'b0;
65 | clkcfg_div_ff <= 0;
66 | ctrl_en_ff <= 1'b0;
67 |
68 | //marker_template_end
69 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin
70 | case (wbs_adr_i)
71 | //marker_template_start
72 | //data: ../data/wfg_drive_spi_reg.json
73 | //template: wishbone/assign_to_registers.template
74 | //marker_template_code
75 |
76 | 4'h4: begin
77 | cfg_core_sel_ff <= wbs_dat_i[5:5];
78 | cfg_cpol_ff <= wbs_dat_i[0:0];
79 | cfg_dff_ff <= wbs_dat_i[3:2];
80 | cfg_lsbfirst_ff <= wbs_dat_i[1:1];
81 | cfg_sspol_ff <= wbs_dat_i[4:4];
82 | end
83 | 4'h8: clkcfg_div_ff <= wbs_dat_i[ 7: 0];
84 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0];
85 |
86 | //marker_template_end
87 | default: begin
88 | end
89 | endcase
90 | end
91 | end
92 |
93 | // Wishbone read from slave
94 | always_ff @(posedge wb_clk_i) begin
95 | if (wb_rst_i) begin
96 | wbs_dat_o <= '0;
97 | end else begin
98 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin
99 | wbs_dat_o <= '0; // default value
100 | case (wbs_adr_i)
101 | //marker_template_start
102 | //data: ../data/wfg_drive_spi_reg.json
103 | //template: wishbone/assign_from_registers.template
104 | //marker_template_code
105 |
106 | 4'h4: begin
107 | wbs_dat_o[5:5] <= cfg_core_sel_ff;
108 | wbs_dat_o[0:0] <= cfg_cpol_ff;
109 | wbs_dat_o[3:2] <= cfg_dff_ff;
110 | wbs_dat_o[1:1] <= cfg_lsbfirst_ff;
111 | wbs_dat_o[4:4] <= cfg_sspol_ff;
112 | end
113 | 4'h8: wbs_dat_o[ 7: 0] <= clkcfg_div_ff;
114 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff;
115 |
116 | //marker_template_end
117 | default: wbs_dat_o <= 'X;
118 | endcase
119 | end
120 | end
121 | end
122 |
123 | // Acknowledgement
124 | always_ff @(posedge wb_clk_i) begin
125 | if (wb_rst_i) wbs_ack_o <= 1'b0;
126 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o;
127 | end
128 |
129 | //marker_template_start
130 | //data: ../data/wfg_drive_spi_reg.json
131 | //template: wishbone/assign_outputs.template
132 | //marker_template_code
133 |
134 | assign cfg_core_sel_q_o = cfg_core_sel_ff;
135 | assign cfg_cpol_q_o = cfg_cpol_ff;
136 | assign cfg_dff_q_o = cfg_dff_ff;
137 | assign cfg_lsbfirst_q_o = cfg_lsbfirst_ff;
138 | assign cfg_sspol_q_o = cfg_sspol_ff;
139 | assign clkcfg_div_q_o = clkcfg_div_ff;
140 | assign ctrl_en_q_o = ctrl_en_ff;
141 |
142 | //marker_template_end
143 | endmodule
144 | `default_nettype wire
145 |
--------------------------------------------------------------------------------
/design/wfg_drive_spi/sim/Makefile:
--------------------------------------------------------------------------------
1 | # source files
2 | SRC := $(wildcard ../rtl/*.sv)
3 | SRC += $(wildcard ../testbench/*.sv)
4 |
5 | # defaults
6 | SIM ?= icarus
7 | TOPLEVEL_LANG ?= verilog
8 |
9 | VERILOG_SOURCES += $(SRC)
10 |
11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
12 | TOPLEVEL = wfg_drive_spi_tb
13 |
14 | # MODULE is the basename of the Python test file
15 | export PYTHONPATH := $(PYTHONPATH):../testbench/
16 | MODULE = test_wfg_drive_spi
17 |
18 | # include cocotb's make rules to take care of the simulator setup
19 | include $(shell cocotb-config --makefiles)/Makefile.sim
20 |
--------------------------------------------------------------------------------
/design/wfg_drive_spi/sim/view.gtkw:
--------------------------------------------------------------------------------
1 | [*]
2 | [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI
3 | [*] Wed May 11 08:07:10 2022
4 | [*]
5 | [dumpfile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_drive_spi/sim/wfg_drive_spi_tb.vcd"
6 | [dumpfile_mtime] "Wed May 11 08:06:37 2022"
7 | [dumpfile_size] 99622
8 | [savefile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_drive_spi/sim/view.gtkw"
9 | [timestart] 0
10 | [size] 1920 1011
11 | [pos] -1 -1
12 | *-23.000000 3060000 -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
13 | [treeopen] wfg_drive_spi_tb.
14 | [treeopen] wfg_drive_spi_tb.wfg_drive_spi_top.
15 | [sst_width] 227
16 | [signals_width] 345
17 | [sst_expanded] 1
18 | [sst_vpaned_height] 295
19 | @200
20 | -Wishbone
21 | @28
22 | wfg_drive_spi_tb.io_wbs_clk
23 | @22
24 | wfg_drive_spi_tb.io_wbs_adr[31:0]
25 | wfg_drive_spi_tb.io_wbs_datrd[31:0]
26 | wfg_drive_spi_tb.io_wbs_datwr[31:0]
27 | @28
28 | wfg_drive_spi_tb.io_wbs_ack
29 | wfg_drive_spi_tb.io_wbs_cyc
30 | wfg_drive_spi_tb.io_wbs_rst
31 | wfg_drive_spi_tb.io_wbs_stb
32 | wfg_drive_spi_tb.io_wbs_we
33 | @200
34 | -AXI
35 | @22
36 | wfg_drive_spi_tb.wfg_drive_spi_axis_tdata[31:0]
37 | @28
38 | wfg_drive_spi_tb.wfg_drive_spi_axis_tlast
39 | wfg_drive_spi_tb.wfg_drive_spi_axis_tready
40 | wfg_drive_spi_tb.wfg_drive_spi_axis_tvalid
41 | @200
42 | -SPI
43 | @29
44 | wfg_drive_spi_tb.wfg_drive_spi_sclk_o
45 | @28
46 | wfg_drive_spi_tb.wfg_drive_spi_cs_no
47 | wfg_drive_spi_tb.wfg_drive_spi_sdo_en_o
48 | wfg_drive_spi_tb.wfg_drive_spi_sdo_o
49 | wfg_drive_spi_tb.wfg_pat_sync_i
50 | wfg_drive_spi_tb.wgf_pat_subcycle_i
51 | @200
52 | -Register
53 | @28
54 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_cpha_ff
55 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_cpol_ff
56 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_dff_ff[5:4]
57 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_lsbfirst_ff
58 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_mstr_ff
59 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_oectrl_ff[11:10]
60 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_ssctrl_ff
61 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_sspol_ff
62 | @22
63 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.clkcfg_div_ff[7:0]
64 | @28
65 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.ctrl_en_ff
66 | @22
67 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.id_peripheral_type_ff[7:0]
68 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.id_version_ff[15:8]
69 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.reginfo_date_ff[17:0]
70 | @28
71 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.test_lpen_ff
72 | [pattern_trace] 1
73 | [pattern_trace] 0
74 |
--------------------------------------------------------------------------------
/design/wfg_drive_spi/testbench/wfg_drive_spi_tb.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `timescale 1ns / 1ps
5 |
6 | `ifdef VERILATOR // make parameter readable from VPI
7 | `define VL_RD /*verilator public_flat_rd*/
8 | `else
9 | `define VL_RD
10 | `endif
11 |
12 | module wfg_drive_spi_tb #(
13 | parameter int BUSW = 32,
14 | parameter int AXIS_DATA_WIDTH = 32
15 | ) (
16 | // Wishbone interface signals
17 | input io_wbs_clk,
18 | input io_wbs_rst,
19 | input [(BUSW-1):0] io_wbs_adr,
20 | input [(BUSW-1):0] io_wbs_datwr,
21 | output [(BUSW-1):0] io_wbs_datrd,
22 | input io_wbs_we,
23 | input io_wbs_stb,
24 | output io_wbs_ack,
25 | input io_wbs_cyc,
26 |
27 | // Bus master A interface
28 | input logic wfg_core_sync_i, // I; Single cycle pulse for write access
29 | input logic wgf_core_subcycle_i, // I; Single cycle pulse for read access
30 |
31 | // AXI-Stream interface
32 | output wire wfg_axis_tready, // O; ready
33 | input logic wfg_axis_tvalid, // I; valid
34 | input logic wfg_axis_tlast, // I; last
35 | input logic [AXIS_DATA_WIDTH-1:0] wfg_axis_tdata, // I; data
36 |
37 | // SPI IO interface
38 | output wire wfg_drive_spi_sclk_o, // O; clock
39 | output wire wfg_drive_spi_cs_no, // O; chip select
40 | output wire wfg_drive_spi_sdo_o, // O; data out
41 | input wire wfg_drive_spi_sdi_i // I; data in, dummy signal for cocotb
42 | );
43 |
44 | wfg_drive_spi_top wfg_drive_spi_top (
45 | .wb_clk_i (io_wbs_clk),
46 | .wb_rst_i (io_wbs_rst),
47 | .wbs_stb_i(io_wbs_stb),
48 | .wbs_cyc_i(io_wbs_cyc),
49 | .wbs_we_i (io_wbs_we),
50 | .wbs_sel_i(4'b1111),
51 | .wbs_dat_i(io_wbs_datwr),
52 | .wbs_adr_i(io_wbs_adr),
53 | .wbs_ack_o(io_wbs_ack),
54 | .wbs_dat_o(io_wbs_datrd),
55 |
56 | .wfg_core_sync_i(wfg_core_sync_i),
57 | .wfg_core_subcycle_i(wgf_core_subcycle_i),
58 |
59 | .wfg_axis_tready_o(wfg_axis_tready),
60 | .wfg_axis_tdata_i (wfg_axis_tdata),
61 | .wfg_axis_tlast_i (wfg_axis_tlast),
62 | .wfg_axis_tvalid_i(wfg_axis_tvalid),
63 |
64 | .wfg_drive_spi_sclk_o(wfg_drive_spi_sclk_o),
65 | .wfg_drive_spi_cs_no (wfg_drive_spi_cs_no),
66 | .wfg_drive_spi_sdo_o (wfg_drive_spi_sdo_o)
67 | );
68 |
69 | // Dump waves
70 | `ifndef VERILATOR
71 | initial begin
72 | $dumpfile("wfg_drive_spi_tb.vcd");
73 | $dumpvars(0, wfg_drive_spi_tb);
74 | end
75 | `endif
76 |
77 | endmodule
78 |
--------------------------------------------------------------------------------
/design/wfg_interconnect/data/wfg_interconnect_reg.csv:
--------------------------------------------------------------------------------
1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description
2 | 4'h0,CTRL,,,,,,,Control register for interconnect
3 | ,,EN,rw,cfg,0,0,1'b0,Interconnect enable
4 | 4'h4,DRIVER0,,,,,,,Driver configuration register
5 | ,,SELECT,rw,cfg,1,0,0,"Select
6 | 0 – wfg_stim_sine
7 | 1 – wfg_stim_mem"
8 | 4'h8,DRIVER1,,,,,,,Driver configuration register
9 | ,,SELECT,rw,cfg,1,0,0,"Select
10 | 0 – wfg_stim_sine
11 | 1 – wfg_stim_mem"
12 |
--------------------------------------------------------------------------------
/design/wfg_interconnect/data/wfg_interconnect_reg.json:
--------------------------------------------------------------------------------
1 | {
2 | "registers": {
3 | "CTRL": {
4 | "address": "4'h0",
5 | "description": "Control register for interconnect",
6 | "entries": {
7 | "EN": {
8 | "LSB": "0",
9 | "MSB": "0",
10 | "access": "rw",
11 | "description": "Interconnect enable",
12 | "hardware": "cfg",
13 | "reset": "1'b0"
14 | }
15 | }
16 | },
17 | "DRIVER0": {
18 | "address": "4'h4",
19 | "description": "Driver configuration register",
20 | "entries": {
21 | "SELECT": {
22 | "LSB": "0",
23 | "MSB": "1",
24 | "access": "rw",
25 | "description": "Select\n0 \u2013 wfg_stim_sine\n1 \u2013 wfg_stim_mem",
26 | "hardware": "cfg",
27 | "reset": "0"
28 | }
29 | }
30 | },
31 | "DRIVER1": {
32 | "address": "4'h8",
33 | "description": "Driver configuration register",
34 | "entries": {
35 | "SELECT": {
36 | "LSB": "0",
37 | "MSB": "1",
38 | "access": "rw",
39 | "description": "Select\n0 \u2013 wfg_stim_sine\n1 \u2013 wfg_stim_mem",
40 | "hardware": "cfg",
41 | "reset": "0"
42 | }
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/design/wfg_interconnect/rtl/wfg_interconnect.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 |
6 | `ifndef WFG_INTERCONNECT_PKG
7 | `define WFG_INTERCONNECT_PKG
8 | typedef struct packed {
9 | logic wfg_axis_tvalid;
10 | logic [31:0] wfg_axis_tdata;
11 | } axis_t;
12 | `endif
13 |
14 | module wfg_interconnect #(
15 | parameter int AXIS_DATA_WIDTH = 32
16 | ) (
17 | input logic clk, // I; System clock
18 | input logic rst_n, // I; active low reset
19 |
20 | // Control
21 | input logic ctrl_en_q_i,
22 |
23 | // Configuration
24 | input logic [1:0] driver0_select_q_i,
25 | input logic [1:0] driver1_select_q_i,
26 |
27 | // Stimuli
28 | input axis_t stimulus_0,
29 | input axis_t stimulus_1,
30 |
31 | output logic wfg_axis_tready_stimulus_0,
32 | output logic wfg_axis_tready_stimulus_1,
33 |
34 | // Driver
35 | output axis_t driver_0,
36 | output axis_t driver_1,
37 |
38 | input wfg_axis_tready_driver_0,
39 | input wfg_axis_tready_driver_1
40 | );
41 |
42 | // Driver 0
43 | always_comb begin
44 | case (driver0_select_q_i)
45 | 2'b00: driver_0 = stimulus_0;
46 | 2'b01: driver_0 = stimulus_1;
47 | 2'b10: driver_0 = '0;
48 | 2'b11: driver_0 = '0;
49 | default: driver_0 = 'x;
50 | endcase
51 | end
52 |
53 | // Driver 1
54 | always_comb begin
55 | case (driver1_select_q_i)
56 | 2'b00: driver_1 = stimulus_0;
57 | 2'b01: driver_1 = stimulus_1;
58 | 2'b10: driver_1 = '0;
59 | 2'b11: driver_1 = '0;
60 | default: driver_1 = 'x;
61 | endcase
62 | end
63 |
64 | // Stimulus 0
65 | always_comb begin
66 | if (driver0_select_q_i == 2'b00 && driver1_select_q_i == 2'b00) begin
67 | wfg_axis_tready_stimulus_0 = wfg_axis_tready_driver_0 && wfg_axis_tready_driver_1;
68 | end else if (driver0_select_q_i == 2'b00) begin
69 | wfg_axis_tready_stimulus_0 = wfg_axis_tready_driver_0;
70 | end else if (driver1_select_q_i == 2'b00) begin
71 | wfg_axis_tready_stimulus_0 = wfg_axis_tready_driver_1;
72 | end else begin
73 | wfg_axis_tready_stimulus_0 = '0;
74 | end
75 | end
76 |
77 | // Stimulus 1
78 | always_comb begin
79 | if (driver0_select_q_i == 2'b01 && driver1_select_q_i == 2'b01) begin
80 | wfg_axis_tready_stimulus_1 = wfg_axis_tready_driver_0 && wfg_axis_tready_driver_1;
81 | end else if (driver0_select_q_i == 2'b01) begin
82 | wfg_axis_tready_stimulus_1 = wfg_axis_tready_driver_0;
83 | end else if (driver1_select_q_i == 2'b01) begin
84 | wfg_axis_tready_stimulus_1 = wfg_axis_tready_driver_1;
85 | end else begin
86 | wfg_axis_tready_stimulus_1 = '0;
87 | end
88 | end
89 |
90 | endmodule
91 | `default_nettype wire
92 |
--------------------------------------------------------------------------------
/design/wfg_interconnect/rtl/wfg_interconnect_pkg.svh:
--------------------------------------------------------------------------------
1 | `ifndef WFG_INTERCONNECT_PKG
2 | `define WFG_INTERCONNECT_PKG
3 |
4 | //package wfg_interconnect_pkg;
5 |
6 | /*interface stimulus_if (input test);
7 | logic wfg_axis_tready;
8 | logic wfg_axis_tvalid;
9 | logic [31:0] wfg_axis_tdata;
10 | endinterface
11 |
12 | interface driver_if (input test);
13 | logic wfg_axis_tready;
14 | logic wfg_axis_tvalid;
15 | logic [31:0] wfg_axis_tdata;
16 | endinterface*/
17 |
18 | typedef struct packed
19 | {
20 | logic wfg_axis_tvalid;
21 | logic [31:0] wfg_axis_tdata;
22 | } stimulus_t;
23 |
24 | typedef struct packed
25 | {
26 | logic wfg_pat_sync;
27 | logic wfg_pat_subcycle;
28 | logic [7:0] wfg_pat_subcycle_cnt;
29 |
30 | logic [31:0] wfg_axis_tdata;
31 | logic wfg_axis_tvalid;
32 | } driver_t;
33 |
34 | //endpackage
35 |
36 | `endif
37 |
--------------------------------------------------------------------------------
/design/wfg_interconnect/rtl/wfg_interconnect_top.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 |
6 | `ifndef WFG_INTERCONNECT_PKG
7 | `define WFG_INTERCONNECT_PKG
8 | typedef struct packed {
9 | logic wfg_axis_tvalid;
10 | logic [31:0] wfg_axis_tdata;
11 | } axis_t;
12 | `endif
13 |
14 | module wfg_interconnect_top #(
15 | parameter int BUSW = 32,
16 | parameter int AXIS_DATA_WIDTH = 32
17 | ) (
18 | // Wishbone Slave ports
19 | input wb_clk_i,
20 | input wb_rst_i,
21 | input wbs_stb_i,
22 | input wbs_cyc_i,
23 | input wbs_we_i,
24 | input [(BUSW/8-1):0] wbs_sel_i,
25 | input [ (BUSW-1):0] wbs_dat_i,
26 | input [ (BUSW-1):0] wbs_adr_i,
27 | output wbs_ack_o,
28 | output [ (BUSW-1):0] wbs_dat_o,
29 |
30 | // Core synchronisation interface
31 | input wire wfg_pat_sync_i, // I; pat_sync pulse
32 | input wire wfg_pat_subcycle_i, // I; subcycle_cnt
33 |
34 | // Stimuli
35 | input axis_t stimulus_0,
36 | input axis_t stimulus_1,
37 |
38 | output wfg_axis_tready_stimulus_0,
39 | output wfg_axis_tready_stimulus_1,
40 |
41 | // Driver
42 | output axis_t driver_0,
43 | output axis_t driver_1,
44 |
45 | input wfg_axis_tready_driver_0,
46 | input wfg_axis_tready_driver_1
47 | );
48 | // Registers
49 | //marker_template_start
50 | //data: ../data/wfg_interconnect_reg.json
51 | //template: wishbone/instantiate_top.template
52 | //marker_template_code
53 |
54 | logic ctrl_en_q; // CTRL.EN register output
55 | logic [ 1: 0] driver0_select_q; // DRIVER0.SELECT register output
56 | logic [ 1: 0] driver1_select_q; // DRIVER1.SELECT register output
57 |
58 | //marker_template_end
59 |
60 | wfg_interconnect_wishbone_reg wfg_interconnect_wishbone_reg (
61 | .wb_clk_i (wb_clk_i),
62 | .wb_rst_i (wb_rst_i),
63 | .wbs_stb_i(wbs_stb_i),
64 | .wbs_cyc_i(wbs_cyc_i),
65 | .wbs_we_i (wbs_we_i),
66 | .wbs_sel_i(wbs_sel_i),
67 | .wbs_dat_i(wbs_dat_i),
68 | .wbs_adr_i(wbs_adr_i),
69 | .wbs_ack_o(wbs_ack_o),
70 | .wbs_dat_o(wbs_dat_o),
71 |
72 | //marker_template_start
73 | //data: ../data/wfg_interconnect_reg.json
74 | //template: wishbone/assign_to_module.template
75 | //marker_template_code
76 |
77 | .ctrl_en_q_o (ctrl_en_q), // CTRL.EN register output
78 | .driver0_select_q_o(driver0_select_q), // DRIVER0.SELECT register output
79 | .driver1_select_q_o(driver1_select_q) // DRIVER1.SELECT register output
80 |
81 | //marker_template_end
82 | );
83 |
84 | wfg_interconnect wfg_interconnect (
85 | .clk (wb_clk_i), // clock signal
86 | .rst_n(!wb_rst_i), // reset signal
87 |
88 | // Control
89 | .ctrl_en_q_i(ctrl_en_q),
90 |
91 | // Configuration
92 | .driver0_select_q_i(driver0_select_q),
93 | .driver1_select_q_i(driver1_select_q),
94 |
95 | .stimulus_0,
96 | .stimulus_1,
97 |
98 | .wfg_axis_tready_stimulus_0,
99 | .wfg_axis_tready_stimulus_1,
100 |
101 | .driver_0,
102 | .driver_1,
103 |
104 | .wfg_axis_tready_driver_0,
105 | .wfg_axis_tready_driver_1
106 | );
107 |
108 | endmodule
109 | `default_nettype wire
110 |
--------------------------------------------------------------------------------
/design/wfg_interconnect/rtl/wfg_interconnect_wishbone_reg.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_interconnect_wishbone_reg #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output logic wbs_ack_o,
18 | output logic [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // Registers
21 | //marker_template_start
22 | //data: ../data/wfg_interconnect_reg.json
23 | //template: wishbone/register_interface.template
24 | //marker_template_code
25 |
26 | output logic ctrl_en_q_o, // CTRL.EN register output
27 | output logic [1:0] driver0_select_q_o, // DRIVER0.SELECT register output
28 | output logic [1:0] driver1_select_q_o // DRIVER1.SELECT register output
29 |
30 | //marker_template_end
31 | );
32 |
33 | //marker_template_start
34 | //data: ../data/wfg_interconnect_reg.json
35 | //template: wishbone/instantiate_registers.template
36 | //marker_template_code
37 |
38 | logic ctrl_en_ff; // CTRL.EN FF
39 | logic [ 1: 0] driver0_select_ff; // DRIVER0.SELECT FF
40 | logic [ 1: 0] driver1_select_ff; // DRIVER1.SELECT FF
41 |
42 | //marker_template_end
43 |
44 | // Wishbone write to slave
45 | always_ff @(posedge wb_clk_i) begin
46 | if (wb_rst_i) begin
47 | //marker_template_start
48 | //data: ../data/wfg_interconnect_reg.json
49 | //template: wishbone/reset_registers.template
50 | //marker_template_code
51 |
52 | ctrl_en_ff <= 1'b0;
53 | driver0_select_ff <= 0;
54 | driver1_select_ff <= 0;
55 |
56 | //marker_template_end
57 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin
58 | case (wbs_adr_i)
59 | //marker_template_start
60 | //data: ../data/wfg_interconnect_reg.json
61 | //template: wishbone/assign_to_registers.template
62 | //marker_template_code
63 |
64 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0];
65 | 4'h4: driver0_select_ff <= wbs_dat_i[ 1: 0];
66 | 4'h8: driver1_select_ff <= wbs_dat_i[ 1: 0];
67 |
68 | //marker_template_end
69 | default: begin
70 | end
71 | endcase
72 | end
73 | end
74 |
75 | // Wishbone read from slave
76 | always_ff @(posedge wb_clk_i) begin
77 | if (wb_rst_i) begin
78 | wbs_dat_o <= '0;
79 | end else begin
80 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin
81 | wbs_dat_o <= '0; // default value
82 | case (wbs_adr_i)
83 | //marker_template_start
84 | //data: ../data/wfg_interconnect_reg.json
85 | //template: wishbone/assign_from_registers.template
86 | //marker_template_code
87 |
88 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff;
89 | 4'h4: wbs_dat_o[ 1: 0] <= driver0_select_ff;
90 | 4'h8: wbs_dat_o[ 1: 0] <= driver1_select_ff;
91 |
92 | //marker_template_end
93 | default: wbs_dat_o <= 'X;
94 | endcase
95 | end
96 | end
97 | end
98 |
99 | // Acknowledgement
100 | always_ff @(posedge wb_clk_i) begin
101 | if (wb_rst_i) wbs_ack_o <= 1'b0;
102 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o;
103 | end
104 |
105 | //marker_template_start
106 | //data: ../data/wfg_interconnect_reg.json
107 | //template: wishbone/assign_outputs.template
108 | //marker_template_code
109 |
110 | assign ctrl_en_q_o = ctrl_en_ff;
111 | assign driver0_select_q_o = driver0_select_ff;
112 | assign driver1_select_q_o = driver1_select_ff;
113 |
114 | //marker_template_end
115 | endmodule
116 | `default_nettype wire
117 |
--------------------------------------------------------------------------------
/design/wfg_interconnect/sim/Makefile:
--------------------------------------------------------------------------------
1 | # source files
2 | SRC := $(wildcard ../rtl/*.sv)
3 | SRC += $(wildcard ../testbench/*.sv)
4 |
5 | # defaults
6 | SIM ?= icarus
7 | TOPLEVEL_LANG ?= verilog
8 |
9 | VERILOG_SOURCES += $(SRC)
10 |
11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
12 | TOPLEVEL = wfg_interconnect_tb
13 |
14 | # MODULE is the basename of the Python test file
15 | export PYTHONPATH := $(PYTHONPATH):../testbench/
16 | MODULE = test_wfg_interconnect
17 |
18 | # include cocotb's make rules to take care of the simulator setup
19 | include $(shell cocotb-config --makefiles)/Makefile.sim
20 |
--------------------------------------------------------------------------------
/design/wfg_interconnect/sim/view.gtkw:
--------------------------------------------------------------------------------
1 | [*]
2 | [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI
3 | [*] Wed May 11 08:07:10 2022
4 | [*]
5 | [dumpfile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_drive_spi/sim/wfg_drive_spi_tb.vcd"
6 | [dumpfile_mtime] "Wed May 11 08:06:37 2022"
7 | [dumpfile_size] 99622
8 | [savefile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_drive_spi/sim/view.gtkw"
9 | [timestart] 0
10 | [size] 1920 1011
11 | [pos] -1 -1
12 | *-23.000000 3060000 -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
13 | [treeopen] wfg_drive_spi_tb.
14 | [treeopen] wfg_drive_spi_tb.wfg_drive_spi_top.
15 | [sst_width] 227
16 | [signals_width] 345
17 | [sst_expanded] 1
18 | [sst_vpaned_height] 295
19 | @200
20 | -Wishbone
21 | @28
22 | wfg_drive_spi_tb.io_wbs_clk
23 | @22
24 | wfg_drive_spi_tb.io_wbs_adr[31:0]
25 | wfg_drive_spi_tb.io_wbs_datrd[31:0]
26 | wfg_drive_spi_tb.io_wbs_datwr[31:0]
27 | @28
28 | wfg_drive_spi_tb.io_wbs_ack
29 | wfg_drive_spi_tb.io_wbs_cyc
30 | wfg_drive_spi_tb.io_wbs_rst
31 | wfg_drive_spi_tb.io_wbs_stb
32 | wfg_drive_spi_tb.io_wbs_we
33 | @200
34 | -AXI
35 | @22
36 | wfg_drive_spi_tb.wfg_drive_spi_axis_tdata[31:0]
37 | @28
38 | wfg_drive_spi_tb.wfg_drive_spi_axis_tlast
39 | wfg_drive_spi_tb.wfg_drive_spi_axis_tready
40 | wfg_drive_spi_tb.wfg_drive_spi_axis_tvalid
41 | @200
42 | -SPI
43 | @29
44 | wfg_drive_spi_tb.wfg_drive_spi_sclk_o
45 | @28
46 | wfg_drive_spi_tb.wfg_drive_spi_cs_no
47 | wfg_drive_spi_tb.wfg_drive_spi_sdo_en_o
48 | wfg_drive_spi_tb.wfg_drive_spi_sdo_o
49 | wfg_drive_spi_tb.wfg_pat_sync_i
50 | wfg_drive_spi_tb.wgf_pat_subcycle_i
51 | @200
52 | -Register
53 | @28
54 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_cpha_ff
55 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_cpol_ff
56 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_dff_ff[5:4]
57 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_lsbfirst_ff
58 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_mstr_ff
59 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_oectrl_ff[11:10]
60 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_ssctrl_ff
61 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.cfg_sspol_ff
62 | @22
63 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.clkcfg_div_ff[7:0]
64 | @28
65 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.ctrl_en_ff
66 | @22
67 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.id_peripheral_type_ff[7:0]
68 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.id_version_ff[15:8]
69 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.reginfo_date_ff[17:0]
70 | @28
71 | wfg_drive_spi_tb.wfg_drive_spi_top.wfg_drive_spi_wishbone_reg.test_lpen_ff
72 | [pattern_trace] 1
73 | [pattern_trace] 0
74 |
--------------------------------------------------------------------------------
/design/wfg_interconnect/testbench/test_wfg_interconnect.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: © 2022 semify
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | import os
5 | import cocotb
6 | from cocotb.clock import Clock
7 | from cocotb.regression import TestFactory
8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge
9 | from cocotbext.wishbone.driver import WishboneMaster
10 | from cocotbext.wishbone.driver import WBOp
11 | from cocotbext.axi import AxiStreamBus, AxiStreamSource, AxiStreamSink
12 | from cocotbext.spi import SpiMaster, SpiSignals, SpiConfig, SpiSlaveBase
13 | #from random import randbytes # Possible in Python 3.9+
14 |
15 | SYSCLK = 100000000
16 | DATA_CNT = 10
17 | NUM_CHANNELS = 2
18 |
19 | short_per = Timer(100, units="ns")
20 | long_time = Timer(100, units="us")
21 |
22 | async def set_register(dut, wbs, address, data):
23 | dut._log.info(f"Set register {address} : {data}")
24 | wbRes = await wbs.send_cycle([WBOp(address, data)])
25 | rvalues = [wb.datrd for wb in wbRes]
26 | dut._log.info(f"Returned values : {rvalues}")
27 |
28 | async def configure(dut, wbs, en=1, select0=0, select1=0):
29 | await set_register(dut, wbs, 0x8, select1)
30 | await set_register(dut, wbs, 0x4, select0)
31 | await set_register(dut, wbs, 0x0, en) # Enable
32 |
33 | async def receive_data(axis_sink, received_data, cnt):
34 | while 1:
35 | data = await axis_sink.recv()
36 | received_data[cnt].append(data.tdata[0])
37 |
38 | @cocotb.coroutine
39 | async def test_interconnect(dut, en, select0, select1):
40 | dut._log.info(f"Configuration: en={en}, select0={select0}, select1={select1}")
41 |
42 | cocotb.start_soon(Clock(dut.io_wbs_clk, 1/SYSCLK*1e9, units="ns").start())
43 |
44 | dut._log.info("Initialize and reset model")
45 |
46 | received_data = [[] for y in range(NUM_CHANNELS)]
47 |
48 | # Start reset
49 | dut.io_wbs_rst.value = 1
50 | dut.stimulus_0.value = 1
51 | await Timer(100, units='ns')
52 |
53 | # Stop reset
54 | dut.io_wbs_rst.value = 0
55 |
56 | # Wishbone Master
57 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk,
58 | width=32, # size of data bus
59 | timeout=10) # in clock cycle number
60 |
61 | # Setup
62 | await configure(dut, wbs, en=en, select0=select0, select1=select1)
63 |
64 | await short_per
65 |
66 | axis_stimulus_0 = AxiStreamSource(AxiStreamBus.from_prefix(dut, "stimulus_0_wfg_axis"), dut.io_wbs_clk, dut.io_wbs_rst)
67 |
68 | axis_stimulus_1 = AxiStreamSource(AxiStreamBus.from_prefix(dut, "stimulus_1_wfg_axis"), dut.io_wbs_clk, dut.io_wbs_rst)
69 |
70 | axis_driver_0 = AxiStreamSink(AxiStreamBus.from_prefix(dut, "driver_0_wfg_axis"), dut.io_wbs_clk, dut.io_wbs_rst)
71 |
72 | axis_driver_1 = AxiStreamSink(AxiStreamBus.from_prefix(dut, "driver_1_wfg_axis"), dut.io_wbs_clk, dut.io_wbs_rst)
73 |
74 | cocotb.start_soon(receive_data(axis_driver_0, received_data, 0))
75 | cocotb.start_soon(receive_data(axis_driver_1, received_data, 1))
76 |
77 | send_data = [[int.from_bytes(os.getrandom(4, os.GRND_NONBLOCK), "big") for _ in range(DATA_CNT)] for y in range(NUM_CHANNELS)]
78 |
79 | for cnt in range(len(send_data)):
80 | for data in send_data[cnt]:
81 |
82 | if cnt == 0:
83 | await axis_stimulus_0.send([data])
84 | if cnt == 1:
85 | await axis_stimulus_1.send([data])
86 |
87 | await short_per
88 |
89 | await short_per
90 |
91 | if select0 == 0 and select1 == 0:
92 | assert(received_data[0] == send_data[0])
93 | assert(received_data[1] == send_data[0])
94 | if select0 == 1 and select1 == 0:
95 | assert(received_data[0] == send_data[1])
96 | assert(received_data[1] == send_data[0])
97 | if select0 == 0 and select1 == 1:
98 | assert(received_data[0] == send_data[0])
99 | assert(received_data[1] == send_data[1])
100 | if select0 == 1 and select1 == 1:
101 | assert(received_data[0] == send_data[1])
102 | assert(received_data[1] == send_data[1])
103 |
104 | factory = TestFactory(test_interconnect)
105 | factory.add_option("en", [1])
106 | factory.add_option("select0", [0, 1])
107 | factory.add_option("select1", [0, 1])
108 |
109 | factory.generate_tests()
110 |
--------------------------------------------------------------------------------
/design/wfg_interconnect/testbench/wfg_interconnect_tb.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `timescale 1ns / 1ps
5 |
6 | `ifdef VERILATOR // make parameter readable from VPI
7 | `define VL_RD /*verilator public_flat_rd*/
8 | `else
9 | `define VL_RD
10 | `endif
11 |
12 | `ifndef WFG_INTERCONNECT_PKG
13 | `define WFG_INTERCONNECT_PKG
14 | typedef struct packed {
15 | logic wfg_axis_tvalid;
16 | logic [31:0] wfg_axis_tdata;
17 | } axis_t;
18 | `endif
19 |
20 | module wfg_interconnect_tb #(
21 | parameter int BUSW = 32,
22 | parameter int AXIS_DATA_WIDTH = 32
23 | ) (
24 | // Wishbone interface signals
25 | input io_wbs_clk,
26 | input io_wbs_rst,
27 | input [(BUSW-1):0] io_wbs_adr,
28 | input [(BUSW-1):0] io_wbs_datwr,
29 | output [(BUSW-1):0] io_wbs_datrd,
30 | input io_wbs_we,
31 | input io_wbs_stb,
32 | output io_wbs_ack,
33 | input io_wbs_cyc,
34 |
35 | // Stimuli
36 | input stimulus_0_wfg_axis_tvalid,
37 | input logic [31:0] stimulus_0_wfg_axis_tdata,
38 |
39 | input stimulus_1_wfg_axis_tvalid,
40 | input logic [31:0] stimulus_1_wfg_axis_tdata,
41 |
42 | output logic stimulus_0_wfg_axis_tready,
43 | output logic stimulus_1_wfg_axis_tready,
44 |
45 | // Driver
46 | output driver_0_wfg_axis_tvalid,
47 | output logic [31:0] driver_0_wfg_axis_tdata,
48 |
49 | output driver_1_wfg_axis_tvalid,
50 | output logic [31:0] driver_1_wfg_axis_tdata,
51 |
52 | input driver_0_wfg_axis_tready,
53 | input driver_1_wfg_axis_tready
54 | );
55 | axis_t stimulus_0;
56 | assign stimulus_0.wfg_axis_tdata = stimulus_0_wfg_axis_tdata;
57 | assign stimulus_0.wfg_axis_tvalid = stimulus_0_wfg_axis_tvalid;
58 |
59 | axis_t stimulus_1;
60 | assign stimulus_1.wfg_axis_tdata = stimulus_1_wfg_axis_tdata;
61 | assign stimulus_1.wfg_axis_tvalid = stimulus_1_wfg_axis_tvalid;
62 |
63 | axis_t driver_0;
64 | assign driver_0_wfg_axis_tdata = driver_0.wfg_axis_tdata;
65 | assign driver_0_wfg_axis_tvalid = driver_0.wfg_axis_tvalid;
66 |
67 | axis_t driver_1;
68 | assign driver_1_wfg_axis_tdata = driver_1.wfg_axis_tdata;
69 | assign driver_1_wfg_axis_tvalid = driver_1.wfg_axis_tvalid;
70 |
71 |
72 | wfg_interconnect_top wfg_interconnect_top (
73 | .wb_clk_i (io_wbs_clk),
74 | .wb_rst_i (io_wbs_rst),
75 | .wbs_stb_i(io_wbs_stb),
76 | .wbs_cyc_i(io_wbs_cyc),
77 | .wbs_we_i (io_wbs_we),
78 | .wbs_sel_i(4'b1111),
79 | .wbs_dat_i(io_wbs_datwr),
80 | .wbs_adr_i(io_wbs_adr),
81 | .wbs_ack_o(io_wbs_ack),
82 | .wbs_dat_o(io_wbs_datrd),
83 |
84 | .stimulus_0,
85 | .stimulus_1,
86 |
87 | .wfg_axis_tready_stimulus_0(stimulus_0_wfg_axis_tready),
88 | .wfg_axis_tready_stimulus_1(stimulus_1_wfg_axis_tready),
89 |
90 | .driver_0,
91 | .driver_1,
92 |
93 | .wfg_axis_tready_driver_0(driver_0_wfg_axis_tready),
94 | .wfg_axis_tready_driver_1(driver_1_wfg_axis_tready)
95 | );
96 |
97 | // Dump waves
98 | `ifndef VERILATOR
99 | initial begin
100 | $dumpfile("wfg_interconnect_tb.vcd");
101 | $dumpvars(0, wfg_interconnect_tb);
102 | end
103 | `endif
104 |
105 | endmodule
106 |
--------------------------------------------------------------------------------
/design/wfg_stim_mem/data/wfg_stim_mem_reg.csv:
--------------------------------------------------------------------------------
1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description
2 | 4'h0,CTRL,,,,,,,Control register for memory unit
3 | ,,EN,rw,cfg,0,0,1'b0,Enables stimuli generation
4 | 4'h4,START,,,,,,,Start register
5 | ,,VAL,rw,cfg,15,0,0,Address start value
6 | 4'h8,END,,,,,,,End register
7 | ,,VAL,rw,cfg,15,0,0,Address end value
8 | 4'hC,CFG,,,,,,,Configuration register
9 | ,,INC,rw,cfg,7,0,8'h01,Increment the address with value
10 | ,,GAIN,rw,cfg,23,8,8'h01,Gain value
11 |
--------------------------------------------------------------------------------
/design/wfg_stim_mem/data/wfg_stim_mem_reg.json:
--------------------------------------------------------------------------------
1 | {
2 | "registers": {
3 | "CFG": {
4 | "address": "4'hC",
5 | "description": "Configuration register",
6 | "entries": {
7 | "GAIN": {
8 | "LSB": "8",
9 | "MSB": "23",
10 | "access": "rw",
11 | "description": "Gain value",
12 | "hardware": "cfg",
13 | "reset": "8'h01"
14 | },
15 | "INC": {
16 | "LSB": "0",
17 | "MSB": "7",
18 | "access": "rw",
19 | "description": "Increment the address with value",
20 | "hardware": "cfg",
21 | "reset": "8'h01"
22 | }
23 | }
24 | },
25 | "CTRL": {
26 | "address": "4'h0",
27 | "description": "Control register for memory unit",
28 | "entries": {
29 | "EN": {
30 | "LSB": "0",
31 | "MSB": "0",
32 | "access": "rw",
33 | "description": "Enables stimuli generation",
34 | "hardware": "cfg",
35 | "reset": "1'b0"
36 | }
37 | }
38 | },
39 | "END": {
40 | "address": "4'h8",
41 | "description": "End register",
42 | "entries": {
43 | "VAL": {
44 | "LSB": "0",
45 | "MSB": "15",
46 | "access": "rw",
47 | "description": "Address end value",
48 | "hardware": "cfg",
49 | "reset": "0"
50 | }
51 | }
52 | },
53 | "START": {
54 | "address": "4'h4",
55 | "description": "Start register",
56 | "entries": {
57 | "VAL": {
58 | "LSB": "0",
59 | "MSB": "15",
60 | "access": "rw",
61 | "description": "Address start value",
62 | "hardware": "cfg",
63 | "reset": "0"
64 | }
65 | }
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/design/wfg_stim_mem/rtl/wfg_stim_mem.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_stim_mem (
6 | input wire clk, // clock signal
7 | input wire rst_n, // reset signal
8 |
9 | input wire wfg_axis_tready_i, // ready signal - AXI
10 | output wire wfg_axis_tvalid_o, // valid signal - AXI
11 | output wire [31:0] wfg_axis_tdata_o, // mem output - AXI
12 |
13 | input wire ctrl_en_q_i, // enable/disable
14 | input logic [15:0] end_val_q_i, // END.VAL register output
15 | input logic [15:0] start_val_q_i, // START.VAL register output
16 | input logic [ 7:0] cfg_inc_q_i, // CFG.INC register output
17 | input logic [15:0] cfg_gain_q_i, // CFG.GAIN register output
18 |
19 | // Memory interface
20 | output csb1,
21 | output [ 9:0] addr1,
22 | input logic [31:0] dout1
23 | );
24 | logic [15:0] cur_address;
25 | logic [31:0] data;
26 | logic [31:0] data_calc;
27 | logic valid;
28 |
29 | assign csb1 = !ctrl_en_q_i; // Enable the memory
30 | assign addr1 = cur_address[9:0]; // Assign address
31 |
32 | assign wfg_axis_tvalid_o = valid;
33 | assign wfg_axis_tdata_o = data;
34 |
35 | typedef enum {
36 | ST_IDLE,
37 | ST_READ,
38 | ST_CALC,
39 | ST_DONE
40 | } wfg_stim_mem_states_t;
41 |
42 | wfg_stim_mem_states_t cur_state;
43 | wfg_stim_mem_states_t next_state;
44 |
45 | always_ff @(posedge clk, negedge rst_n) begin
46 | if (!rst_n) cur_state <= ST_IDLE;
47 | else cur_state <= next_state;
48 | end
49 |
50 | always_comb begin
51 | next_state = cur_state;
52 | case (cur_state)
53 | ST_IDLE: begin
54 | if (ctrl_en_q_i) next_state = ST_READ;
55 | end
56 | ST_READ: begin
57 | next_state = ST_CALC;
58 | end
59 | ST_CALC: begin
60 | next_state = ST_DONE;
61 | end
62 | ST_DONE: begin
63 | if (wfg_axis_tready_i == 1'b1) next_state = ST_IDLE;
64 | end
65 | endcase
66 | end
67 |
68 | always_ff @(posedge clk, negedge rst_n) begin
69 | if (!rst_n) begin
70 | cur_address <= '0;
71 | valid <= '0;
72 | data <= '0;
73 | end else begin
74 | valid <= '0;
75 |
76 | case (cur_state)
77 | ST_IDLE: begin
78 | if (!ctrl_en_q_i) begin
79 | cur_address <= start_val_q_i;
80 | end
81 | end
82 | ST_READ: begin
83 | if (cur_address + cfg_inc_q_i > end_val_q_i) begin
84 | cur_address <= start_val_q_i;
85 | end else begin
86 | cur_address <= cur_address + cfg_inc_q_i;
87 | end
88 | end
89 | ST_CALC: begin
90 | data <= data_calc;
91 | end
92 | ST_DONE: begin
93 | valid <= '1;
94 | end
95 | default: valid <= 'x;
96 | endcase
97 | end
98 | end
99 |
100 | dsp_scale_sn_us #(
101 | .DATA_W(32),
102 | .SCALE_W(16),
103 | .SCALE_SHFT(0)
104 | ) dsp_scale_sn_us_inst (
105 | .clk (clk), // I; System clock
106 | .reset_ni(rst_n), // I; active loaw reset
107 |
108 | // Data interface
109 | .scale_gif_data_in_update_i(next_state == ST_CALC), // I; GIF update pulse
110 | .scale_gif_data_in_i (dout1), // I; GIF data to be scaled (signed)
111 | .scale_factor_i (cfg_gain_q_i), // I; Scaling factor (unsigned)
112 | .scale_gif_result_update_o (), // O; GIF update pulse
113 | .scale_gif_result_o (data_calc)
114 | );
115 |
116 | endmodule
117 | `default_nettype wire
118 |
--------------------------------------------------------------------------------
/design/wfg_stim_mem/rtl/wfg_stim_mem_top.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_stim_mem_top #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output wbs_ack_o,
18 | output [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // AXI-Stream interface
21 | input wfg_axis_tready_i,
22 | output wfg_axis_tvalid_o,
23 | output [31:0] wfg_axis_tdata_o,
24 |
25 | // Memory interface
26 | output csb1,
27 | output [ 9:0] addr1,
28 | input [31:0] dout1
29 | );
30 | // Registers
31 | //marker_template_start
32 | //data: ../data/wfg_stim_mem_reg.json
33 | //template: wishbone/instantiate_top.template
34 | //marker_template_code
35 |
36 | logic [23: 8] cfg_gain_q; // CFG.GAIN register output
37 | logic [ 7: 0] cfg_inc_q; // CFG.INC register output
38 | logic ctrl_en_q; // CTRL.EN register output
39 | logic [15: 0] end_val_q; // END.VAL register output
40 | logic [15: 0] start_val_q; // START.VAL register output
41 |
42 | //marker_template_end
43 |
44 | wfg_stim_mem_wishbone_reg wfg_stim_mem_wishbone_reg (
45 | .wb_clk_i (wb_clk_i),
46 | .wb_rst_i (wb_rst_i),
47 | .wbs_stb_i(wbs_stb_i),
48 | .wbs_cyc_i(wbs_cyc_i),
49 | .wbs_we_i (wbs_we_i),
50 | .wbs_sel_i(wbs_sel_i),
51 | .wbs_dat_i(wbs_dat_i),
52 | .wbs_adr_i(wbs_adr_i),
53 | .wbs_ack_o(wbs_ack_o),
54 | .wbs_dat_o(wbs_dat_o),
55 |
56 | //marker_template_start
57 | //data: ../data/wfg_stim_mem_reg.json
58 | //template: wishbone/assign_to_module.template
59 | //marker_template_code
60 |
61 | .cfg_gain_q_o (cfg_gain_q), // CFG.GAIN register output
62 | .cfg_inc_q_o (cfg_inc_q), // CFG.INC register output
63 | .ctrl_en_q_o (ctrl_en_q), // CTRL.EN register output
64 | .end_val_q_o (end_val_q), // END.VAL register output
65 | .start_val_q_o(start_val_q) // START.VAL register output
66 |
67 | //marker_template_end
68 | );
69 |
70 | wfg_stim_mem wfg_stim_mem (
71 | .clk (wb_clk_i), // clock signal
72 | .rst_n (!wb_rst_i), // reset signal
73 | .wfg_axis_tready_i(wfg_axis_tready_i), // ready signal - AXI
74 | .wfg_axis_tvalid_o(wfg_axis_tvalid_o), // valid signal - AXI
75 | .wfg_axis_tdata_o (wfg_axis_tdata_o), // mem output - AXI
76 | .ctrl_en_q_i (ctrl_en_q), // enable/disable
77 | .end_val_q_i (end_val_q),
78 | .start_val_q_i (start_val_q),
79 | .cfg_inc_q_i (cfg_inc_q),
80 | .cfg_gain_q_i (cfg_gain_q),
81 | .csb1 (csb1),
82 | .addr1 (addr1),
83 | .dout1 (dout1)
84 | );
85 |
86 | endmodule
87 | `default_nettype wire
88 |
--------------------------------------------------------------------------------
/design/wfg_stim_mem/rtl/wfg_stim_mem_wishbone_reg.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_stim_mem_wishbone_reg #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output logic wbs_ack_o,
18 | output logic [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // Registers
21 | //marker_template_start
22 | //data: ../data/wfg_stim_mem_reg.json
23 | //template: wishbone/register_interface.template
24 | //marker_template_code
25 |
26 | output logic [23:8] cfg_gain_q_o, // CFG.GAIN register output
27 | output logic [ 7:0] cfg_inc_q_o, // CFG.INC register output
28 | output logic ctrl_en_q_o, // CTRL.EN register output
29 | output logic [15:0] end_val_q_o, // END.VAL register output
30 | output logic [15:0] start_val_q_o // START.VAL register output
31 |
32 | //marker_template_end
33 | );
34 |
35 | //marker_template_start
36 | //data: ../data/wfg_stim_mem_reg.json
37 | //template: wishbone/instantiate_registers.template
38 | //marker_template_code
39 |
40 | logic [23: 8] cfg_gain_ff; // CFG.GAIN FF
41 | logic [ 7: 0] cfg_inc_ff; // CFG.INC FF
42 | logic ctrl_en_ff; // CTRL.EN FF
43 | logic [15: 0] end_val_ff; // END.VAL FF
44 | logic [15: 0] start_val_ff; // START.VAL FF
45 |
46 | //marker_template_end
47 |
48 | // Wishbone write to slave
49 | always_ff @(posedge wb_clk_i) begin
50 | if (wb_rst_i) begin
51 | //marker_template_start
52 | //data: ../data/wfg_stim_mem_reg.json
53 | //template: wishbone/reset_registers.template
54 | //marker_template_code
55 |
56 | cfg_gain_ff <= 8'h01;
57 | cfg_inc_ff <= 8'h01;
58 | ctrl_en_ff <= 1'b0;
59 | end_val_ff <= 0;
60 | start_val_ff <= 0;
61 |
62 | //marker_template_end
63 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin
64 | case (wbs_adr_i)
65 | //marker_template_start
66 | //data: ../data/wfg_stim_mem_reg.json
67 | //template: wishbone/assign_to_registers.template
68 | //marker_template_code
69 |
70 | 4'hC: begin
71 | cfg_gain_ff <= wbs_dat_i[23:8];
72 | cfg_inc_ff <= wbs_dat_i[7:0];
73 | end
74 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0];
75 | 4'h8: end_val_ff <= wbs_dat_i[15: 0];
76 | 4'h4: start_val_ff <= wbs_dat_i[15: 0];
77 |
78 | //marker_template_end
79 | default: begin
80 | end
81 | endcase
82 | end
83 | end
84 |
85 | // Wishbone read from slave
86 | always_ff @(posedge wb_clk_i) begin
87 | if (wb_rst_i) begin
88 | wbs_dat_o <= '0;
89 | end else begin
90 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin
91 | wbs_dat_o <= '0; // default value
92 | case (wbs_adr_i)
93 | //marker_template_start
94 | //data: ../data/wfg_stim_mem_reg.json
95 | //template: wishbone/assign_from_registers.template
96 | //marker_template_code
97 |
98 | 4'hC: begin
99 | wbs_dat_o[23:8] <= cfg_gain_ff;
100 | wbs_dat_o[7:0] <= cfg_inc_ff;
101 | end
102 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff;
103 | 4'h8: wbs_dat_o[15: 0] <= end_val_ff;
104 | 4'h4: wbs_dat_o[15: 0] <= start_val_ff;
105 |
106 | //marker_template_end
107 | default: wbs_dat_o <= 'X;
108 | endcase
109 | end
110 | end
111 | end
112 |
113 | // Acknowledgement
114 | always_ff @(posedge wb_clk_i) begin
115 | if (wb_rst_i) wbs_ack_o <= 1'b0;
116 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o;
117 | end
118 |
119 | //marker_template_start
120 | //data: ../data/wfg_stim_mem_reg.json
121 | //template: wishbone/assign_outputs.template
122 | //marker_template_code
123 |
124 | assign cfg_gain_q_o = cfg_gain_ff;
125 | assign cfg_inc_q_o = cfg_inc_ff;
126 | assign ctrl_en_q_o = ctrl_en_ff;
127 | assign end_val_q_o = end_val_ff;
128 | assign start_val_q_o = start_val_ff;
129 |
130 | //marker_template_end
131 | endmodule
132 | `default_nettype wire
133 |
--------------------------------------------------------------------------------
/design/wfg_stim_mem/sim/Makefile:
--------------------------------------------------------------------------------
1 | # source files
2 | SRC := $(wildcard ../rtl/*.sv)
3 | SRC += $(wildcard ../testbench/*.sv)
4 |
5 | # defaults
6 | SIM ?= icarus
7 | TOPLEVEL_LANG ?= verilog
8 |
9 | VERILOG_SOURCES += $(SRC)
10 |
11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
12 | TOPLEVEL = wfg_stim_mem_tb
13 |
14 | # MODULE is the basename of the Python test file
15 | export PYTHONPATH := $(PYTHONPATH):../testbench/
16 | MODULE = test_wfg_stim_mem
17 |
18 | # include cocotb's make rules to take care of the simulator setup
19 | include $(shell cocotb-config --makefiles)/Makefile.sim
20 |
--------------------------------------------------------------------------------
/design/wfg_stim_mem/sim/memory.hex:
--------------------------------------------------------------------------------
1 | 00000000
2 | 00000001
3 | 00000002
4 | 00000003
5 | 00000004
6 | 00000005
7 | 00000006
8 | 00000007
9 | 00000008
10 | 00000009
11 | 0000000A
12 | 0000000B
13 | 0000000C
14 | 0000000D
15 | 0000000E
16 | 0000000F
17 | 00000010
18 | 00000011
19 | 00000012
20 | 00000013
21 | 00000014
22 | 00000015
23 | 00000016
24 | 00000017
25 | 00000018
26 | 00000019
27 | 0000001A
28 | 0000001B
29 | 0000001C
30 | 0000001D
31 | 0000001E
32 | 0000001F
33 |
--------------------------------------------------------------------------------
/design/wfg_stim_mem/testbench/test_wfg_stim_mem.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: © 2022 semify
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | import math
5 | import statistics
6 | import cocotb
7 | from cocotb.clock import Clock
8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge
9 | from cocotb.regression import TestFactory
10 | from cocotbext.wishbone.driver import WishboneMaster
11 | from cocotbext.wishbone.driver import WBOp
12 |
13 | DATA_CNT = 10
14 |
15 | short_per = Timer(100, units="ns")
16 | long_time = Timer(100, units="us")
17 |
18 | async def set_register(dut, wbs, peripheral_address, address, data):
19 | if address > 0xF:
20 | dut._log.error("Can not access peripheral registers outside 0xF")
21 |
22 | real_address = (peripheral_address<<4) | (address & 0xF)
23 | dut._log.info(f"Set register {real_address} : {data}")
24 |
25 | wbRes = await wbs.send_cycle([WBOp(real_address, data)])
26 | rvalues = [wb.datrd for wb in wbRes]
27 | dut._log.info(f"Returned values : {rvalues}")
28 |
29 | async def configure_stim_mem(dut, wbs, en, start=0x0000, end=0x0000, inc=0x01, gain=0x0001):
30 | await set_register(dut, wbs, 0x0, 0x4, start)
31 | await set_register(dut, wbs, 0x0, 0x8, end)
32 | await set_register(dut, wbs, 0x0, 0xC, gain<<8 | inc)
33 | await set_register(dut, wbs, 0x0, 0x0, en) # Enable
34 |
35 | @cocotb.coroutine
36 | async def mem_test(dut, start=0x0000, end=0x0000, inc=0x01, gain=0x0001):
37 | cocotb.start_soon(Clock(dut.io_wbs_clk, 10, units="ns").start())
38 |
39 | dut._log.info("Initialize and reset model")
40 |
41 | # Start reset
42 | dut.io_wbs_rst.value = 1
43 | dut.wfg_axis_tready.value = 1
44 |
45 | await Timer(100, units='ns')
46 |
47 | # Stop reset
48 | dut.io_wbs_rst.value = 0
49 |
50 | # Wishbone Master
51 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk,
52 | width=32, # size of data bus
53 | timeout=10) # in clock cycle number
54 |
55 | await short_per
56 |
57 | dut._log.info("Configure stim_mem")
58 | await configure_stim_mem(dut, wbs, en=1, start=start, end=end, inc=inc, gain=gain)
59 |
60 | cur_address = start
61 |
62 | # Gather data
63 | for i in range(DATA_CNT):
64 | await FallingEdge(dut.wfg_axis_tvalid)
65 | value = dut.wfg_axis_tdata.value
66 |
67 | dut._log.info(f"Test: {cur_address} == {value}")
68 |
69 | assert(cur_address*gain == value)
70 |
71 | cur_address += inc
72 |
73 | if (cur_address > end):
74 | cur_address = start
75 |
76 | factory = TestFactory(mem_test)
77 |
78 | factory.add_option("start", [0x0000, 0x0010])
79 | factory.add_option("end", [0x0005, 0x001F])
80 | factory.add_option("inc", [0x01, 0x02])
81 | factory.add_option("gain", [0x01, 0x04])
82 |
83 | factory.generate_tests()
84 |
--------------------------------------------------------------------------------
/design/wfg_stim_mem/testbench/wfg_stim_mem_tb.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `timescale 1ns / 1ps
5 |
6 | `ifdef VERILATOR // make parameter readable from VPI
7 | `define VL_RD /*verilator public_flat_rd*/
8 | `else
9 | `define VL_RD
10 | `endif
11 |
12 | module wfg_stim_mem_tb #(
13 | parameter int BUSW = 32
14 | ) (
15 | // Wishbone interface signals
16 | input io_wbs_clk,
17 | input io_wbs_rst,
18 | input [(BUSW-1):0] io_wbs_adr,
19 | input [(BUSW-1):0] io_wbs_datwr,
20 | output [(BUSW-1):0] io_wbs_datrd,
21 | input io_wbs_we,
22 | input io_wbs_stb,
23 | output io_wbs_ack,
24 | input io_wbs_cyc,
25 |
26 | // AXI-Stream interface
27 | input wfg_axis_tready,
28 | output wfg_axis_tvalid,
29 | output [31:0] wfg_axis_tdata
30 | );
31 |
32 | logic csb1;
33 | logic [9:0 ] addr1;
34 | logic [31:0] dout1;
35 |
36 | localparam MEM_SIZE = 2 ** 10;
37 |
38 | logic [31:0] mem[MEM_SIZE];
39 |
40 | wfg_stim_mem_top wfg_stim_mem_top (
41 | .wb_clk_i (io_wbs_clk),
42 | .wb_rst_i (io_wbs_rst),
43 | .wbs_stb_i(io_wbs_stb),
44 | .wbs_cyc_i(io_wbs_cyc),
45 | .wbs_we_i (io_wbs_we),
46 | .wbs_sel_i(4'b1111),
47 | .wbs_dat_i(io_wbs_datwr),
48 | .wbs_adr_i(io_wbs_adr),
49 | .wbs_ack_o(io_wbs_ack),
50 | .wbs_dat_o(io_wbs_datrd),
51 |
52 | .wfg_axis_tready_i(wfg_axis_tready),
53 | .wfg_axis_tvalid_o(wfg_axis_tvalid),
54 | .wfg_axis_tdata_o (wfg_axis_tdata),
55 |
56 | .csb1 (csb1),
57 | .addr1(addr1),
58 | .dout1(dout1)
59 | );
60 |
61 | initial begin
62 | $readmemh("memory.hex", mem);
63 | end
64 |
65 | always_ff @(negedge io_wbs_clk) begin
66 | if (!csb1) dout1 <= mem[addr1];
67 | end
68 |
69 | // Dump waves
70 | `ifndef VERILATOR
71 | initial begin
72 | $dumpfile("wfg_stim_mem_tb.vcd");
73 | $dumpvars(0, wfg_stim_mem_tb);
74 | end
75 | `endif
76 |
77 | endmodule
78 |
--------------------------------------------------------------------------------
/design/wfg_stim_sine/README.md:
--------------------------------------------------------------------------------
1 | # Documentation
2 |
3 | ## Overview
4 |
5 | The Sine Wave Pattern Generator serves as a data generator that provides data for the AXI streaming interface. The sine wave generator, as the name implies, generates values in the form of sine waves. The values are generated from the angular phase, which is controlled by the `inc` (increment) register. Other parameters that influence the sine wave are gain, multiplier with a range of 0 - 1.999..., and offset with a range of -2 to 1.999....
6 |
7 | ## RTL Implementation - CORDIC Algorithm
8 |
9 | CORDIC (COordinate Rotation DIgital Computer) is a hardware-efficient iterative method that uses rotations to compute a wide range of elementary functions. The RTL uses the CORDIC algorithm as a primitive method for generating sinusoidal values from phase. The behavior of the CORDIC algorithm is controlled by the following input registers:
10 | - inc_val_q_i (16bit) - phase increment value
11 | - gain_val_q_i (16bit) - gain value (0 - 1.999..)
12 | - offset_val_q_i (18bit) - offset value (-2 - 1.999..)
13 |
14 | The output is represented by the following registers:
15 | - sine_out (18bit) - sine value (-2 - 1.999...) calculated from the input parameters.
16 |
17 | Some other inputs are:
18 | - clk - system clock
19 | - rst - reset
20 | - ctrl_en_q_i - enable/disable data generation
21 |
22 | The output is a 2f16 signed value with a frequency of 100*MHz*.
23 |
24 | ## Cocotb Testbench
25 |
26 | Cocotb is used for the testbench environment. It allows the user to access all RTL signals, control them and influence their behavior. In this case, the testbench accesses and modifies the phase increment value (`inc_val_q_i`), the gain factor (`gain_val_q_i`) and the offset (`offset_val_q_i`), as well as the system clock, reset and data generation enable. On the other hand, the testbench receives sine values from RTL via the `sine_out` output line. Just as RTL performs the calculation of the sine value using the CORDIC algorithm, the Testbench can import the *sin()* function from the *math* library. With the corresponding calculation, the sine value is output in hexadecimal form. The verification of the values calculated in the testbench and the values from the CORDIC algorithm is explained in detail in the following section.
--------------------------------------------------------------------------------
/design/wfg_stim_sine/data/wfg_stim_sine_reg.csv:
--------------------------------------------------------------------------------
1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description
2 | 4'h0,CTRL,,,,,,,Control register for sine wave generation unit
3 | ,,EN,rw,cfg,0,0,1'b0,Enables stimuli generation
4 | 4'h4,INC,,,,,,,Increment register
5 | ,,VAL,rw,cfg,15,0,16'h1000,Increment for angle per sample (f=fs/2**16/INC). Counter is implemented as wrap-around counter.
6 | 4'h8,GAIN,,,,,,,Gain register
7 | ,,VAL,rw,cfg,15,0,16'h4000,Sine wave gain in 2f14 unsigned representation.
8 | 4'hC,OFFSET,,,,,,,Offset register
9 | ,,VAL,rw,cfg,17,0,18'h0000,Signed offset added after applying gain.
10 |
--------------------------------------------------------------------------------
/design/wfg_stim_sine/data/wfg_stim_sine_reg.json:
--------------------------------------------------------------------------------
1 | {
2 | "registers": {
3 | "CTRL": {
4 | "address": "4'h0",
5 | "description": "Control register for sine wave generation unit",
6 | "entries": {
7 | "EN": {
8 | "LSB": "0",
9 | "MSB": "0",
10 | "access": "rw",
11 | "description": "Enables stimuli generation",
12 | "hardware": "cfg",
13 | "reset": "1'b0"
14 | }
15 | }
16 | },
17 | "GAIN": {
18 | "address": "4'h8",
19 | "description": "Gain register",
20 | "entries": {
21 | "VAL": {
22 | "LSB": "0",
23 | "MSB": "15",
24 | "access": "rw",
25 | "description": "Sine wave gain in 2f14 unsigned representation. ",
26 | "hardware": "cfg",
27 | "reset": "16'h4000"
28 | }
29 | }
30 | },
31 | "INC": {
32 | "address": "4'h4",
33 | "description": "Increment register",
34 | "entries": {
35 | "VAL": {
36 | "LSB": "0",
37 | "MSB": "15",
38 | "access": "rw",
39 | "description": "Increment for angle per sample (f=fs/2**16/INC). Counter is implemented as wrap-around counter.",
40 | "hardware": "cfg",
41 | "reset": "16'h1000"
42 | }
43 | }
44 | },
45 | "OFFSET": {
46 | "address": "4'hC",
47 | "description": "Offset register",
48 | "entries": {
49 | "VAL": {
50 | "LSB": "0",
51 | "MSB": "17",
52 | "access": "rw",
53 | "description": "Signed offset added after applying gain.",
54 | "hardware": "cfg",
55 | "reset": "18'h0000"
56 | }
57 | }
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/design/wfg_stim_sine/rtl/wfg_stim_sine_top.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_stim_sine_top #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output wbs_ack_o,
18 | output [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // AXI-Stream interface
21 | input wfg_axis_tready_i,
22 | output wfg_axis_tvalid_o,
23 | output signed [31:0] wfg_axis_tdata_o
24 | );
25 | // Registers
26 | //marker_template_start
27 | //data: ../data/wfg_stim_sine_reg.json
28 | //template: wishbone/instantiate_top.template
29 | //marker_template_code
30 |
31 | logic ctrl_en_q; // CTRL.EN register output
32 | logic [15: 0] gain_val_q; // GAIN.VAL register output
33 | logic [15: 0] inc_val_q; // INC.VAL register output
34 | logic [17: 0] offset_val_q; // OFFSET.VAL register output
35 |
36 | //marker_template_end
37 |
38 | wfg_stim_sine_wishbone_reg wfg_stim_sine_wishbone_reg (
39 | .wb_clk_i (wb_clk_i),
40 | .wb_rst_i (wb_rst_i),
41 | .wbs_stb_i(wbs_stb_i),
42 | .wbs_cyc_i(wbs_cyc_i),
43 | .wbs_we_i (wbs_we_i),
44 | .wbs_sel_i(wbs_sel_i),
45 | .wbs_dat_i(wbs_dat_i),
46 | .wbs_adr_i(wbs_adr_i),
47 | .wbs_ack_o(wbs_ack_o),
48 | .wbs_dat_o(wbs_dat_o),
49 |
50 | //marker_template_start
51 | //data: ../data/wfg_stim_sine_reg.json
52 | //template: wishbone/assign_to_module.template
53 | //marker_template_code
54 |
55 | .ctrl_en_q_o (ctrl_en_q), // CTRL.EN register output
56 | .gain_val_q_o (gain_val_q), // GAIN.VAL register output
57 | .inc_val_q_o (inc_val_q), // INC.VAL register output
58 | .offset_val_q_o(offset_val_q) // OFFSET.VAL register output
59 |
60 | //marker_template_end
61 | );
62 |
63 | wfg_stim_sine wfg_stim_sine (
64 | .clk (wb_clk_i), // clock signal
65 | .rst_n (!wb_rst_i), // reset signal
66 | .wfg_axis_tready_i(wfg_axis_tready_i), // ready signal - AXI
67 | .wfg_axis_tvalid_o(wfg_axis_tvalid_o), // valid signal - AXI
68 | .wfg_axis_tdata_o (wfg_axis_tdata_o), // sine output - AXI
69 | .ctrl_en_q_i (ctrl_en_q), // enable/disable simulation
70 | .inc_val_q_i (inc_val_q), // angular increment
71 | .gain_val_q_i (gain_val_q), // sine gain/multiplier
72 | .offset_val_q_i (offset_val_q) // sine offset
73 | );
74 |
75 | endmodule
76 | `default_nettype wire
77 |
--------------------------------------------------------------------------------
/design/wfg_stim_sine/rtl/wfg_stim_sine_wishbone_reg.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_stim_sine_wishbone_reg #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output logic wbs_ack_o,
18 | output logic [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // Registers
21 | //marker_template_start
22 | //data: ../data/wfg_stim_sine_reg.json
23 | //template: wishbone/register_interface.template
24 | //marker_template_code
25 |
26 | output logic ctrl_en_q_o, // CTRL.EN register output
27 | output logic [15:0] gain_val_q_o, // GAIN.VAL register output
28 | output logic [15:0] inc_val_q_o, // INC.VAL register output
29 | output logic [17:0] offset_val_q_o // OFFSET.VAL register output
30 |
31 | //marker_template_end
32 | );
33 |
34 | //marker_template_start
35 | //data: ../data/wfg_stim_sine_reg.json
36 | //template: wishbone/instantiate_registers.template
37 | //marker_template_code
38 |
39 | logic ctrl_en_ff; // CTRL.EN FF
40 | logic [15: 0] gain_val_ff; // GAIN.VAL FF
41 | logic [15: 0] inc_val_ff; // INC.VAL FF
42 | logic [17: 0] offset_val_ff; // OFFSET.VAL FF
43 |
44 | //marker_template_end
45 |
46 | // Wishbone write to slave
47 | always_ff @(posedge wb_clk_i) begin
48 | if (wb_rst_i) begin
49 | //marker_template_start
50 | //data: ../data/wfg_stim_sine_reg.json
51 | //template: wishbone/reset_registers.template
52 | //marker_template_code
53 |
54 | ctrl_en_ff <= 1'b0;
55 | gain_val_ff <= 16'h4000;
56 | inc_val_ff <= 16'h1000;
57 | offset_val_ff <= 18'h0000;
58 |
59 | //marker_template_end
60 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin
61 | case (wbs_adr_i)
62 | //marker_template_start
63 | //data: ../data/wfg_stim_sine_reg.json
64 | //template: wishbone/assign_to_registers.template
65 | //marker_template_code
66 |
67 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0];
68 | 4'h8: gain_val_ff <= wbs_dat_i[15: 0];
69 | 4'h4: inc_val_ff <= wbs_dat_i[15: 0];
70 | 4'hC: offset_val_ff <= wbs_dat_i[17: 0];
71 |
72 | //marker_template_end
73 | default: begin
74 | end
75 | endcase
76 | end
77 | end
78 |
79 | // Wishbone read from slave
80 | always_ff @(posedge wb_clk_i) begin
81 | if (wb_rst_i) begin
82 | wbs_dat_o <= '0;
83 | end else begin
84 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin
85 | wbs_dat_o <= '0; // default value
86 | case (wbs_adr_i)
87 | //marker_template_start
88 | //data: ../data/wfg_stim_sine_reg.json
89 | //template: wishbone/assign_from_registers.template
90 | //marker_template_code
91 |
92 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff;
93 | 4'h8: wbs_dat_o[15: 0] <= gain_val_ff;
94 | 4'h4: wbs_dat_o[15: 0] <= inc_val_ff;
95 | 4'hC: wbs_dat_o[17: 0] <= offset_val_ff;
96 |
97 | //marker_template_end
98 | default: wbs_dat_o <= 'X;
99 | endcase
100 | end
101 | end
102 | end
103 |
104 | // Acknowledgement
105 | always_ff @(posedge wb_clk_i) begin
106 | if (wb_rst_i) wbs_ack_o <= 1'b0;
107 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o;
108 | end
109 |
110 | //marker_template_start
111 | //data: ../data/wfg_stim_sine_reg.json
112 | //template: wishbone/assign_outputs.template
113 | //marker_template_code
114 |
115 | assign ctrl_en_q_o = ctrl_en_ff;
116 | assign gain_val_q_o = gain_val_ff;
117 | assign inc_val_q_o = inc_val_ff;
118 | assign offset_val_q_o = offset_val_ff;
119 |
120 | //marker_template_end
121 | endmodule
122 | `default_nettype wire
123 |
--------------------------------------------------------------------------------
/design/wfg_stim_sine/sim/Makefile:
--------------------------------------------------------------------------------
1 | # source files
2 | SRC := $(wildcard ../rtl/*.sv)
3 | SRC += $(wildcard ../testbench/*.sv)
4 |
5 | # defaults
6 | SIM ?= icarus
7 | TOPLEVEL_LANG ?= verilog
8 |
9 | VERILOG_SOURCES += $(SRC)
10 |
11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
12 | TOPLEVEL = wfg_stim_sine_tb
13 |
14 | # MODULE is the basename of the Python test file
15 | export PYTHONPATH := $(PYTHONPATH):../testbench/
16 | MODULE = test_wfg_stim_sine
17 |
18 | # include cocotb's make rules to take care of the simulator setup
19 | include $(shell cocotb-config --makefiles)/Makefile.sim
20 |
--------------------------------------------------------------------------------
/design/wfg_stim_sine/testbench/test_wfg_stim_sine.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: © 2022 semify
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | import math
5 | import statistics
6 | import cocotb
7 | from cocotb.clock import Clock
8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge
9 | from cocotb.regression import TestFactory
10 | from cocotbext.wishbone.driver import WishboneMaster
11 | from cocotbext.wishbone.driver import WBOp
12 |
13 | DATA_CNT = 10
14 |
15 | short_per = Timer(100, units="ns")
16 | long_time = Timer(100, units="us")
17 |
18 | async def set_register(dut, wbs, peripheral_address, address, data):
19 | if address > 0xF:
20 | dut._log.error("Can not access peripheral registers outside 0xF")
21 |
22 | real_address = (peripheral_address<<4) | (address & 0xF)
23 | dut._log.info(f"Set register {real_address} : {data}")
24 |
25 | wbRes = await wbs.send_cycle([WBOp(real_address, data)])
26 | rvalues = [wb.datrd for wb in wbRes]
27 | dut._log.info(f"Returned values : {rvalues}")
28 |
29 | async def configure_stim_sine(dut, wbs, en, inc=0x1000, gain=0x4000, offset=0):
30 | await set_register(dut, wbs, 0x0, 0x4, inc)
31 | await set_register(dut, wbs, 0x0, 0x8, gain)
32 | await set_register(dut, wbs, 0x0, 0xC, offset)
33 | await set_register(dut, wbs, 0x0, 0x0, en) # Enable
34 |
35 | @cocotb.coroutine
36 | async def sine_test(dut, sine_inc=0x1000, sine_gain=0x4000, sine_offset=0):
37 | cocotb.start_soon(Clock(dut.io_wbs_clk, 10, units="ns").start())
38 |
39 | dut._log.info("Initialize and reset model")
40 |
41 | # Start reset
42 | dut.io_wbs_rst.value = 1
43 | dut.wfg_axis_tready.value = 1
44 |
45 | await Timer(100, units='ns')
46 |
47 | # Stop reset
48 | dut.io_wbs_rst.value = 0
49 |
50 | # Wishbone Master
51 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk,
52 | width=32, # size of data bus
53 | timeout=10) # in clock cycle number
54 |
55 | await short_per
56 |
57 | dut._log.info("Configure stim_sine")
58 | await configure_stim_sine(dut, wbs, en=1, inc=sine_inc, gain=sine_gain, offset=sine_offset)
59 |
60 | num_values = int((2**16) / sine_inc + 1)
61 | y_data = []
62 |
63 | # Gather data
64 | for i in range(num_values):
65 | await FallingEdge(dut.wfg_axis_tvalid)
66 | value = int(dut.wfg_axis_tdata.value)
67 |
68 | # Sign extend
69 | if value & (1<<17):
70 | value |= ((1<<14)-1)<<18
71 |
72 | value = value.to_bytes(4, 'big')
73 | value = int.from_bytes(value, 'big', signed=True)
74 | y_data.append(value)
75 |
76 | y_data_float = []
77 |
78 | y_error = []
79 | y_squared_error = []
80 | y_absolute_error = []
81 |
82 | # Compare results
83 | for (cnt, value) in enumerate(y_data):
84 | input_val = cnt * sine_inc
85 | while input_val >= (2**16):
86 | input_val -= (2**16)
87 |
88 | angle_rad = float(input_val) / (2**16) * 2 * math.pi
89 | calculated_value = math.sin(angle_rad) * (sine_gain / 2**14) + (sine_offset/2**16)
90 | output_as_float = float(value) / (2**16)
91 | y_data_float.append(output_as_float)
92 | y_error.append(output_as_float - calculated_value)
93 | y_squared_error.append((output_as_float - calculated_value)**2)
94 | y_absolute_error.append(abs(output_as_float - calculated_value))
95 |
96 | y_mean_squared_error = statistics.mean(y_squared_error)
97 | dut._log.info("y_mean_squared_error: {}".format(y_mean_squared_error))
98 |
99 | y_mean_absolute_error = statistics.mean(y_absolute_error)
100 | dut._log.info("y_mean_absolute_error: {}".format(y_mean_absolute_error))
101 |
102 | assert(y_mean_absolute_error < 0.001)
103 |
104 | factory = TestFactory(sine_test)
105 |
106 | factory.add_option("sine_inc", [0x500, 0x1000])
107 | factory.add_option("sine_gain", [0x4000, 0x2000, 0x6000])
108 | factory.add_option("sine_offset", [0x0000, 0x8000])
109 |
110 | factory.generate_tests()
111 |
--------------------------------------------------------------------------------
/design/wfg_stim_sine/testbench/wfg_stim_sine_tb.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `timescale 1ns / 1ps
5 |
6 | `ifdef VERILATOR // make parameter readable from VPI
7 | `define VL_RD /*verilator public_flat_rd*/
8 | `else
9 | `define VL_RD
10 | `endif
11 |
12 | module wfg_stim_sine_tb #(
13 | parameter int BUSW = 32
14 | ) (
15 | // Wishbone interface signals
16 | input io_wbs_clk,
17 | input io_wbs_rst,
18 | input [(BUSW-1):0] io_wbs_adr,
19 | input [(BUSW-1):0] io_wbs_datwr,
20 | output [(BUSW-1):0] io_wbs_datrd,
21 | input io_wbs_we,
22 | input io_wbs_stb,
23 | output io_wbs_ack,
24 | input io_wbs_cyc,
25 |
26 | // AXI-Stream interface
27 | input wfg_axis_tready,
28 | output wfg_axis_tvalid,
29 | output signed [17:0] wfg_axis_tdata
30 | );
31 |
32 | wfg_stim_sine_top wfg_stim_sine_top (
33 | .wb_clk_i (io_wbs_clk),
34 | .wb_rst_i (io_wbs_rst),
35 | .wbs_stb_i(io_wbs_stb),
36 | .wbs_cyc_i(io_wbs_cyc),
37 | .wbs_we_i (io_wbs_we),
38 | .wbs_sel_i(4'b1111),
39 | .wbs_dat_i(io_wbs_datwr),
40 | .wbs_adr_i(io_wbs_adr),
41 | .wbs_ack_o(io_wbs_ack),
42 | .wbs_dat_o(io_wbs_datrd),
43 |
44 | .wfg_axis_tready_i(wfg_axis_tready),
45 | .wfg_axis_tvalid_o(wfg_axis_tvalid),
46 | .wfg_axis_tdata_o (wfg_axis_tdata)
47 | );
48 |
49 | // Dump waves
50 | `ifndef VERILATOR
51 | initial begin
52 | $dumpfile("wfg_stim_sine_tb.vcd");
53 | $dumpvars(0, wfg_stim_sine_tb);
54 | end
55 | `endif
56 |
57 | endmodule
58 |
--------------------------------------------------------------------------------
/design/wfg_subcore/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | *Copyright © 2021* [Semify EDA](
4 | https://github.com/semify-eda)
5 |
6 | ## wfg_subcore documentation
7 | #### Overview
8 | This module is responsible to synchronize all implemented modules, ensuring data transfers with a set frequency.
9 | According to the configuration parameter wfg_pat_subcycle, clock cycles are counted and a subcycle pulse is generated. This subcycle enables finer division of the synchronization pulse and is used for synchronizations with phase shifts. The configuration parameter wfg_pat_sync specifies the number of subcycle pulses during a sync period. The resulting sync frequency
10 |
11 | 
12 |
13 | can be determined using these two parameters. The outputs of the module are subcycle and sync pulses, as well as three optional status signals (active, start pulse, subcyle counter).
--------------------------------------------------------------------------------
/design/wfg_subcore/data/wfg_subcore_reg.csv:
--------------------------------------------------------------------------------
1 | Address,RegName,BitName,Access,HW,MSB,LSB,Reset,Description
2 | 4'h0,CTRL,,,,,,,Core control register
3 | ,,EN,rw,cfg,0,0,1'b0,Core enable
4 | 4'h4,CFG,,,,,,,Core configuration register
5 | ,,SYNC,rw,cfg,7,0,0,"Sync pulse clock divider.
6 | Count clk until threshold is reached"
7 | ,,SUBCYCLE,rw,cfg,23,8,0,"Subcycle pulse clock divider.
8 | Count clk until threshold is reached"
9 |
--------------------------------------------------------------------------------
/design/wfg_subcore/data/wfg_subcore_reg.json:
--------------------------------------------------------------------------------
1 | {
2 | "registers": {
3 | "CFG": {
4 | "address": "4'h4",
5 | "description": "Core configuration register",
6 | "entries": {
7 | "SUBCYCLE": {
8 | "LSB": "8",
9 | "MSB": "23",
10 | "access": "rw",
11 | "description": "Subcycle pulse clock divider.\nCount clk until threshold is reached",
12 | "hardware": "cfg",
13 | "reset": "0"
14 | },
15 | "SYNC": {
16 | "LSB": "0",
17 | "MSB": "7",
18 | "access": "rw",
19 | "description": "Sync pulse clock divider.\nCount clk until threshold is reached",
20 | "hardware": "cfg",
21 | "reset": "0"
22 | }
23 | }
24 | },
25 | "CTRL": {
26 | "address": "4'h0",
27 | "description": "Core control register",
28 | "entries": {
29 | "EN": {
30 | "LSB": "0",
31 | "MSB": "0",
32 | "access": "rw",
33 | "description": "Core enable",
34 | "hardware": "cfg",
35 | "reset": "1'b0"
36 | }
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/design/wfg_subcore/formula.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/design/wfg_subcore/formula.png
--------------------------------------------------------------------------------
/design/wfg_subcore/rtl/wfg_subcore.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_subcore (
6 | input wire clk, // I; System clock
7 | input wire rst_n, // I; Active low reset
8 | input wire en_i, // I; Enable signal
9 |
10 | // Config
11 | input wire [ 7:0] wfg_sync_count_i, // I; Sync counter threshold
12 | input wire [15:0] wfg_subcycle_count_i, // I; Subcycle counter threshold
13 |
14 | // Signals
15 | output wire wfg_subcore_sync_o, // O; Sync signal
16 | output wire wfg_subcore_subcycle_o, // O; Subcycle signal
17 | output wire wfg_subcore_start_o, // O; Indicate start
18 | output wire [7:0] wfg_subcore_subcycle_cnt_o, // O; Subcycle pulse counter
19 | output wire active_o // O; Active indication signal
20 | );
21 |
22 | // -------------------------------------------------------------------------
23 | // Definition
24 | // -------------------------------------------------------------------------
25 |
26 | logic [15:0] subcycle_count; // L; counting variable
27 | logic [ 7:0] sync_count; // L; counting variable
28 | logic temp_subcycle; // L; Internal subcycle
29 | logic temp_sync; // L; Internal subcycle
30 | logic subcycle_dly; // L; Rising edge det subcycle
31 | logic sync_dly; // L; Rising edge det sync
32 | logic en_i_dly; // L; Rising edge det enable
33 | logic [ 7:0] subcycle_pls_cnt; // L; Counts Subcycles
34 |
35 | // -------------------------------------------------------------------------
36 | // Implementation
37 | // -------------------------------------------------------------------------
38 |
39 | always @(posedge clk, negedge rst_n) begin
40 |
41 | if (~rst_n) begin
42 | subcycle_count <= 16'd0;
43 | sync_count <= 8'd0;
44 | temp_subcycle <= 1'b0;
45 | temp_sync <= 1'b0;
46 | subcycle_pls_cnt <= 8'd0;
47 | end else begin
48 |
49 | if (en_i) begin
50 |
51 | if (subcycle_count > 0) begin
52 | temp_subcycle <= temp_subcycle;
53 | subcycle_count <= subcycle_count - 1;
54 | end else begin
55 | temp_subcycle <= ~temp_subcycle;
56 | subcycle_count <= (wfg_subcycle_count_i);
57 |
58 | if (sync_count > 0) begin
59 | temp_sync <= temp_sync;
60 | sync_count <= sync_count - 1;
61 | end else begin
62 | temp_sync <= ~temp_sync;
63 | sync_count <= (wfg_sync_count_i);
64 | end
65 |
66 | end
67 |
68 | if (wfg_subcore_subcycle_o) begin
69 | subcycle_pls_cnt <= subcycle_pls_cnt + 1;
70 | end
71 |
72 | end else begin
73 | subcycle_count <= 8'd0;
74 | sync_count <= 8'd0;
75 | temp_subcycle <= 1'b0;
76 | temp_sync <= 1'b0;
77 | subcycle_pls_cnt <= 8'd0;
78 | end
79 |
80 | subcycle_dly <= temp_subcycle;
81 | sync_dly <= temp_sync;
82 | en_i_dly <= en_i;
83 |
84 | if (wfg_subcore_sync_o) begin
85 | subcycle_pls_cnt <= 8'd0;
86 | end
87 |
88 | end
89 |
90 | end
91 |
92 | assign wfg_subcore_subcycle_o = temp_subcycle & ~subcycle_dly;
93 | assign wfg_subcore_sync_o = temp_sync & ~sync_dly;
94 | assign active_o = en_i;
95 | assign wfg_subcore_start_o = en_i & ~en_i_dly;
96 | assign wfg_subcore_subcycle_cnt_o = subcycle_pls_cnt;
97 |
98 | endmodule
99 | `default_nettype wire
100 |
--------------------------------------------------------------------------------
/design/wfg_subcore/rtl/wfg_subcore_top.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_subcore_top #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output wbs_ack_o,
18 | output [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // subcore synchronisation interface
21 | output wire wfg_subcore_sync_o, // O; Sync signal
22 | output wire wfg_subcore_subcycle_o, // O; Subcycle signal
23 | output wire wfg_subcore_start_o, // O; Indicate start
24 | output wire [7:0] wfg_subcore_subcycle_cnt_o, // O; Subcycle pulse counter
25 | output wire active_o // O; Active indication signal
26 | );
27 | // Registers
28 | //marker_template_start
29 | //data: ../data/wfg_subcore_reg.json
30 | //template: wishbone/instantiate_top.template
31 | //marker_template_code
32 |
33 | logic [23: 8] cfg_subcycle_q; // CFG.SUBCYCLE register output
34 | logic [ 7: 0] cfg_sync_q; // CFG.SYNC register output
35 | logic ctrl_en_q; // CTRL.EN register output
36 |
37 | //marker_template_end
38 |
39 | wfg_subcore_wishbone_reg wfg_subcore_wishbone_reg (
40 | .wb_clk_i (wb_clk_i),
41 | .wb_rst_i (wb_rst_i),
42 | .wbs_stb_i(wbs_stb_i),
43 | .wbs_cyc_i(wbs_cyc_i),
44 | .wbs_we_i (wbs_we_i),
45 | .wbs_sel_i(wbs_sel_i),
46 | .wbs_dat_i(wbs_dat_i),
47 | .wbs_adr_i(wbs_adr_i),
48 | .wbs_ack_o(wbs_ack_o),
49 | .wbs_dat_o(wbs_dat_o),
50 |
51 | //marker_template_start
52 | //data: ../data/wfg_subcore_reg.json
53 | //template: wishbone/assign_to_module.template
54 | //marker_template_code
55 |
56 | .cfg_subcycle_q_o(cfg_subcycle_q), // CFG.SUBCYCLE register output
57 | .cfg_sync_q_o (cfg_sync_q), // CFG.SYNC register output
58 | .ctrl_en_q_o (ctrl_en_q) // CTRL.EN register output
59 |
60 | //marker_template_end
61 | );
62 |
63 | wfg_subcore wfg_subcore (
64 | .clk (wb_clk_i), // clock signal
65 | .rst_n(!wb_rst_i), // reset signal
66 |
67 | // Control
68 | .en_i(ctrl_en_q), // I; Enable signal
69 |
70 | // Configuration
71 | .wfg_sync_count_i (cfg_sync_q), // I; Sync counter threshold
72 | .wfg_subcycle_count_i(cfg_subcycle_q), // I: Subcycle counter threshold
73 |
74 | // Output
75 | .wfg_subcore_sync_o (wfg_subcore_sync_o), // O; Sync signal
76 | .wfg_subcore_subcycle_o (wfg_subcore_subcycle_o), // O; Subcycle signal
77 | .wfg_subcore_start_o (wfg_subcore_start_o), // O; Indicate start
78 | .wfg_subcore_subcycle_cnt_o(wfg_subcore_subcycle_cnt_o), // O; Subcycle pulse counter
79 | .active_o (active_o) // O; Active indication signal
80 | );
81 |
82 | endmodule
83 | `default_nettype wire
84 |
--------------------------------------------------------------------------------
/design/wfg_subcore/rtl/wfg_subcore_wishbone_reg.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `default_nettype none
5 | module wfg_subcore_wishbone_reg #(
6 | parameter int BUSW = 32
7 | ) (
8 | // Wishbone Slave ports
9 | input wb_clk_i,
10 | input wb_rst_i,
11 | input wbs_stb_i,
12 | input wbs_cyc_i,
13 | input wbs_we_i,
14 | input [(BUSW/8-1):0] wbs_sel_i,
15 | input [ (BUSW-1):0] wbs_dat_i,
16 | input [ (BUSW-1):0] wbs_adr_i,
17 | output logic wbs_ack_o,
18 | output logic [ (BUSW-1):0] wbs_dat_o,
19 |
20 | // Registers
21 | //marker_template_start
22 | //data: ../data/wfg_subcore_reg.json
23 | //template: wishbone/register_interface.template
24 | //marker_template_code
25 |
26 | output logic [23:8] cfg_subcycle_q_o, // CFG.SUBCYCLE register output
27 | output logic [ 7:0] cfg_sync_q_o, // CFG.SYNC register output
28 | output logic ctrl_en_q_o // CTRL.EN register output
29 |
30 | //marker_template_end
31 | );
32 |
33 | //marker_template_start
34 | //data: ../data/wfg_subcore_reg.json
35 | //template: wishbone/instantiate_registers.template
36 | //marker_template_code
37 |
38 | logic [23: 8] cfg_subcycle_ff; // CFG.SUBCYCLE FF
39 | logic [ 7: 0] cfg_sync_ff; // CFG.SYNC FF
40 | logic ctrl_en_ff; // CTRL.EN FF
41 |
42 | //marker_template_end
43 |
44 | // Wishbone write to slave
45 | always_ff @(posedge wb_clk_i) begin
46 | if (wb_rst_i) begin
47 | //marker_template_start
48 | //data: ../data/wfg_subcore_reg.json
49 | //template: wishbone/reset_registers.template
50 | //marker_template_code
51 |
52 | cfg_subcycle_ff <= 0;
53 | cfg_sync_ff <= 0;
54 | ctrl_en_ff <= 1'b0;
55 |
56 | //marker_template_end
57 | end else if (wbs_stb_i && wbs_we_i && wbs_cyc_i) begin
58 | case (wbs_adr_i)
59 | //marker_template_start
60 | //data: ../data/wfg_subcore_reg.json
61 | //template: wishbone/assign_to_registers.template
62 | //marker_template_code
63 |
64 | 4'h4: begin
65 | cfg_subcycle_ff <= wbs_dat_i[23:8];
66 | cfg_sync_ff <= wbs_dat_i[7:0];
67 | end
68 | 4'h0: ctrl_en_ff <= wbs_dat_i[ 0: 0];
69 |
70 | //marker_template_end
71 | default: begin
72 | end
73 | endcase
74 | end
75 | end
76 |
77 | // Wishbone read from slave
78 | always_ff @(posedge wb_clk_i) begin
79 | if (wb_rst_i) begin
80 | wbs_dat_o <= '0;
81 | end else begin
82 | if (wbs_stb_i && !wbs_we_i && wbs_cyc_i) begin
83 | wbs_dat_o <= '0; // default value
84 | case (wbs_adr_i)
85 | //marker_template_start
86 | //data: ../data/wfg_subcore_reg.json
87 | //template: wishbone/assign_from_registers.template
88 | //marker_template_code
89 |
90 | 4'h4: begin
91 | wbs_dat_o[23:8] <= cfg_subcycle_ff;
92 | wbs_dat_o[7:0] <= cfg_sync_ff;
93 | end
94 | 4'h0: wbs_dat_o[ 0: 0] <= ctrl_en_ff;
95 |
96 | //marker_template_end
97 | default: wbs_dat_o <= 'X;
98 | endcase
99 | end
100 | end
101 | end
102 |
103 | // Acknowledgement
104 | always_ff @(posedge wb_clk_i) begin
105 | if (wb_rst_i) wbs_ack_o <= 1'b0;
106 | else wbs_ack_o <= wbs_stb_i && wbs_cyc_i & !wbs_ack_o;
107 | end
108 |
109 | //marker_template_start
110 | //data: ../data/wfg_subcore_reg.json
111 | //template: wishbone/assign_outputs.template
112 | //marker_template_code
113 |
114 | assign cfg_subcycle_q_o = cfg_subcycle_ff;
115 | assign cfg_sync_q_o = cfg_sync_ff;
116 | assign ctrl_en_q_o = ctrl_en_ff;
117 |
118 | //marker_template_end
119 | endmodule
120 | `default_nettype wire
121 |
--------------------------------------------------------------------------------
/design/wfg_subcore/sim/Makefile:
--------------------------------------------------------------------------------
1 | # source files
2 | SRC := $(wildcard ../rtl/*.sv)
3 | SRC += $(wildcard ../testbench/*.sv)
4 |
5 | # defaults
6 | SIM ?= icarus
7 | TOPLEVEL_LANG ?= verilog
8 |
9 | VERILOG_SOURCES += $(SRC)
10 |
11 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
12 | TOPLEVEL = wfg_subcore_tb
13 |
14 | # MODULE is the basename of the Python test file
15 | export PYTHONPATH := $(PYTHONPATH):../testbench/
16 | MODULE = test_wfg_subcore
17 |
18 | # include cocotb's make rules to take care of the simulator setup
19 | include $(shell cocotb-config --makefiles)/Makefile.sim
20 |
--------------------------------------------------------------------------------
/design/wfg_subcore/sim/view.gtkw:
--------------------------------------------------------------------------------
1 | [*]
2 | [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI
3 | [*] Fri May 13 06:43:30 2022
4 | [*]
5 | [dumpfile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_core/sim/wfg_core_tb.vcd"
6 | [dumpfile_mtime] "Fri May 13 06:30:51 2022"
7 | [dumpfile_size] 1052480
8 | [savefile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_core/sim/view.gtkw"
9 | [timestart] 0
10 | [size] 1776 1043
11 | [pos] -1 -1
12 | *-20.000000 810000 -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
13 | [treeopen] wfg_core_tb.
14 | [treeopen] wfg_core_tb.wfg_core_top.
15 | [sst_width] 227
16 | [signals_width] 488
17 | [sst_expanded] 1
18 | [sst_vpaned_height] 410
19 | @200
20 | -Wishbone
21 | @28
22 | wfg_core_tb.io_wbs_clk
23 | @22
24 | wfg_core_tb.io_wbs_adr[31:0]
25 | wfg_core_tb.io_wbs_datrd[31:0]
26 | wfg_core_tb.io_wbs_datwr[31:0]
27 | @28
28 | wfg_core_tb.io_wbs_ack
29 | wfg_core_tb.io_wbs_cyc
30 | wfg_core_tb.io_wbs_rst
31 | wfg_core_tb.io_wbs_stb
32 | wfg_core_tb.io_wbs_we
33 | @200
34 | -Core
35 | @28
36 | wfg_core_tb.wfg_pat_start_o
37 | wfg_core_tb.wfg_pat_subcycle_o
38 | wfg_core_tb.wfg_pat_sync_o
39 | @22
40 | wfg_core_tb.wfg_pat_subcycle_cnt_o[7:0]
41 | @28
42 | wfg_core_tb.active_o
43 | @200
44 | -Internals
45 | @28
46 | wfg_core_tb.wfg_core_top.wfg_core.active_o
47 | wfg_core_tb.wfg_core_top.wfg_core.clk
48 | wfg_core_tb.wfg_core_top.wfg_core.en_i
49 | wfg_core_tb.wfg_core_top.wfg_core.en_i_dly
50 | wfg_core_tb.wfg_core_top.wfg_core.rst_n
51 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_start_o
52 | @22
53 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_count_ff[15:0]
54 | @28
55 | wfg_core_tb.wfg_core_top.wfg_core.temp_subcycle_ff
56 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_dly
57 | @22
58 | wfg_core_tb.wfg_core_top.wfg_core.subcycle_pls_cnt[7:0]
59 | wfg_core_tb.wfg_core_top.wfg_core.sync_count_ff[7:0]
60 | @28
61 | wfg_core_tb.wfg_core_top.wfg_core.temp_sync_ff
62 | wfg_core_tb.wfg_core_top.wfg_core.sync_dly
63 | @22
64 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_subcycle_cnt_o[7:0]
65 | @28
66 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_subcycle_o
67 | wfg_core_tb.wfg_core_top.wfg_core.wfg_pat_sync_o
68 | @22
69 | wfg_core_tb.wfg_core_top.wfg_core.wfg_subcycle_count_i[15:0]
70 | wfg_core_tb.wfg_core_top.wfg_core.wfg_sync_count_i[7:0]
71 | [pattern_trace] 1
72 | [pattern_trace] 0
73 |
--------------------------------------------------------------------------------
/design/wfg_subcore/testbench/test_wfg_core.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: © 2022 semify
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | import random
5 | import cocotb
6 | from cocotb.clock import Clock
7 | from cocotb.regression import TestFactory
8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge, ClockCycles
9 | from cocotbext.wishbone.driver import WishboneMaster
10 | from cocotbext.wishbone.driver import WBOp
11 |
12 | CLK_PER_SYNC = 300
13 | SYSCLK = 100000000
14 | DATA_CNT = 10
15 |
16 | short_per = Timer(100, units="ns")
17 | long_time = Timer(100, units="us")
18 |
19 | async def set_register(dut, wbs, address, data):
20 | dut._log.info(f"Set register {address} : {data}")
21 |
22 | wbRes = await wbs.send_cycle([WBOp(address, data)])
23 |
24 | rvalues = [wb.datrd for wb in wbRes]
25 | dut._log.info(f"Returned values : {rvalues}")
26 |
27 | async def configure(dut, wbs, en, sync_count, subcycle_count):
28 | await set_register(dut, wbs, 0x4, (sync_count << 0) | (subcycle_count << 8))
29 | await set_register(dut, wbs, 0x0, en) # Enable core
30 |
31 | @cocotb.coroutine
32 | async def core_test(dut, en, sync_count, subcycle_count):
33 | dut._log.info(f"Configuration: sync_count={sync_count}, subcycle_count={subcycle_count}")
34 |
35 | cocotb.start_soon(Clock(dut.io_wbs_clk, 1/SYSCLK*1e9, units="ns").start())
36 |
37 | dut._log.info("Initialize and reset model")
38 |
39 | # Start reset
40 | dut.io_wbs_rst.value = 1
41 | await Timer(100, units='ns')
42 |
43 | # Stop reset
44 | dut.io_wbs_rst.value = 0
45 |
46 | # Wishbone Master
47 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk,
48 | width=32, # size of data bus
49 | timeout=10) # in clock cycle number
50 |
51 | # Setup core
52 | await configure(dut, wbs, en, sync_count, subcycle_count)
53 |
54 | #await short_per
55 | #await short_per
56 |
57 | sync_pulse_count = 0
58 | clk_count = 0
59 |
60 | await FallingEdge(dut.wfg_core_sync_o)
61 |
62 | for i in range ((sync_count + 1) * (subcycle_count + 1) * 3):
63 | await ClockCycles(dut.io_wbs_clk, 1)
64 | clk_count += 1
65 |
66 | if dut.wfg_core_sync_o == 1:
67 | assert ((sync_pulse_count+1) * clk_count) == ((sync_count + 1) * (subcycle_count + 1) * 2)
68 | break
69 |
70 | if (dut.wfg_core_subcycle_o == 1):
71 | sync_pulse_count += 1
72 | clk_count = 0
73 |
74 | length = 2
75 |
76 | sync_array = []
77 |
78 | for i in range(length):
79 | n = random.randint(1,2**7)
80 | sync_array.append(n)
81 |
82 | subcycle_array = []
83 |
84 | for i in range(length):
85 | n = random.randint(1,2**7)
86 | subcycle_array.append(n)
87 |
88 |
89 | factory = TestFactory(core_test)
90 | factory.add_option("en", [1])
91 | factory.add_option("sync_count", sync_array)
92 | factory.add_option("subcycle_count", subcycle_array)
93 |
94 | factory.generate_tests()
95 |
--------------------------------------------------------------------------------
/design/wfg_subcore/testbench/test_wfg_subcore.py:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: © 2022 semify
2 | # SPDX-License-Identifier: Apache-2.0
3 |
4 | import random
5 | import cocotb
6 | from cocotb.clock import Clock
7 | from cocotb.regression import TestFactory
8 | from cocotb.triggers import Timer, RisingEdge, FallingEdge, ClockCycles
9 | from cocotbext.wishbone.driver import WishboneMaster
10 | from cocotbext.wishbone.driver import WBOp
11 |
12 | CLK_PER_SYNC = 300
13 | SYSCLK = 100000000
14 | DATA_CNT = 10
15 |
16 | short_per = Timer(100, units="ns")
17 | long_time = Timer(100, units="us")
18 |
19 | async def set_register(dut, wbs, address, data):
20 | dut._log.info(f"Set register {address} : {data}")
21 |
22 | wbRes = await wbs.send_cycle([WBOp(address, data)])
23 |
24 | rvalues = [wb.datrd for wb in wbRes]
25 | dut._log.info(f"Returned values : {rvalues}")
26 |
27 | async def configure(dut, wbs, en, sync_count, subcycle_count):
28 | await set_register(dut, wbs, 0x4, (sync_count << 0) | (subcycle_count << 8))
29 | await set_register(dut, wbs, 0x0, en) # Enable subcore
30 |
31 | @cocotb.coroutine
32 | async def subcore_test(dut, en, sync_count, subcycle_count):
33 | dut._log.info(f"Configuration: sync_count={sync_count}, subcycle_count={subcycle_count}")
34 |
35 | cocotb.start_soon(Clock(dut.io_wbs_clk, 1/SYSCLK*1e9, units="ns").start())
36 |
37 | dut._log.info("Initialize and reset model")
38 |
39 | # Start reset
40 | dut.io_wbs_rst.value = 1
41 | await Timer(100, units='ns')
42 |
43 | # Stop reset
44 | dut.io_wbs_rst.value = 0
45 |
46 | # Wishbone Master
47 | wbs = WishboneMaster(dut, "io_wbs", dut.io_wbs_clk,
48 | width=32, # size of data bus
49 | timeout=10) # in clock cycle number
50 |
51 | # Setup subcore
52 | await configure(dut, wbs, en, sync_count, subcycle_count)
53 |
54 | #await short_per
55 | #await short_per
56 |
57 | sync_pulse_count = 0
58 | clk_count = 0
59 |
60 | await FallingEdge(dut.wfg_subcore_sync_o)
61 |
62 | for i in range ((sync_count + 1) * (subcycle_count + 1) * 3):
63 | await ClockCycles(dut.io_wbs_clk, 1)
64 | clk_count += 1
65 |
66 | if dut.wfg_subcore_sync_o == 1:
67 | assert ((sync_pulse_count+1) * clk_count) == ((sync_count + 1) * (subcycle_count + 1) * 2)
68 | break
69 |
70 | if (dut.wfg_subcore_subcycle_o == 1):
71 | sync_pulse_count += 1
72 | clk_count = 0
73 |
74 | length = 2
75 |
76 | sync_array = []
77 |
78 | for i in range(length):
79 | n = random.randint(1,2**7)
80 | sync_array.append(n)
81 |
82 | subcycle_array = []
83 |
84 | for i in range(length):
85 | n = random.randint(1,2**7)
86 | subcycle_array.append(n)
87 |
88 |
89 | factory = TestFactory(subcore_test)
90 | factory.add_option("en", [1])
91 | factory.add_option("sync_count", sync_array)
92 | factory.add_option("subcycle_count", subcycle_array)
93 |
94 | factory.generate_tests()
95 |
--------------------------------------------------------------------------------
/design/wfg_subcore/testbench/wfg_subcore_tb.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `timescale 1ns / 1ps
5 |
6 | `ifdef VERILATOR // make parameter readable from VPI
7 | `define VL_RD /*verilator public_flat_rd*/
8 | `else
9 | `define VL_RD
10 | `endif
11 |
12 | module wfg_subcore_tb #(
13 | parameter int BUSW = 32
14 | ) (
15 | // Wishbone interface signals
16 | input io_wbs_clk,
17 | input io_wbs_rst,
18 | input [(BUSW-1):0] io_wbs_adr,
19 | input [(BUSW-1):0] io_wbs_datwr,
20 | output [(BUSW-1):0] io_wbs_datrd,
21 | input io_wbs_we,
22 | input io_wbs_stb,
23 | output io_wbs_ack,
24 | input io_wbs_cyc,
25 |
26 | // subcore synchronisation interface
27 | output wire wfg_subcore_sync_o, // O; Sync signal
28 | output wire wfg_subcore_subcycle_o, // O; Subcycle signal
29 | output wire wfg_subcore_start_o, // O; Indicate start
30 | output wire [7:0] wfg_subcore_subcycle_cnt_o, // O; Subcycle pulse counter
31 | output wire active_o // O; Active indication signal
32 | );
33 |
34 | wfg_subcore_top wfg_subcore_top (
35 | .wb_clk_i (io_wbs_clk),
36 | .wb_rst_i (io_wbs_rst),
37 | .wbs_stb_i(io_wbs_stb),
38 | .wbs_cyc_i(io_wbs_cyc),
39 | .wbs_we_i (io_wbs_we),
40 | .wbs_sel_i(4'b1111),
41 | .wbs_dat_i(io_wbs_datwr),
42 | .wbs_adr_i(io_wbs_adr),
43 | .wbs_ack_o(io_wbs_ack),
44 | .wbs_dat_o(io_wbs_datrd),
45 |
46 | // subcore synchronisation interface
47 | .wfg_subcore_sync_o(wfg_subcore_sync_o),
48 | .wfg_subcore_subcycle_o(wfg_subcore_subcycle_o),
49 | .wfg_subcore_start_o(wfg_subcore_start_o),
50 | .wfg_subcore_subcycle_cnt_o(wfg_subcore_subcycle_cnt_o),
51 | .active_o(active_o)
52 | );
53 |
54 | // Dump waves
55 | `ifndef VERILATOR
56 | initial begin
57 | $dumpfile("wfg_subcore_tb.vcd");
58 | $dumpvars(0, wfg_subcore_tb);
59 | end
60 | `endif
61 |
62 | endmodule
63 |
--------------------------------------------------------------------------------
/design/wfg_top/sim/Makefile:
--------------------------------------------------------------------------------
1 | # source files
2 | SRC := $(wildcard ../rtl/*.sv)
3 | SRC += $(wildcard ../testbench/*.sv)
4 | SRC += $(wildcard ../../wfg_core/rtl/*.sv)
5 | SRC += $(wildcard ../../wfg_subcore/rtl/*.sv)
6 | SRC += $(wildcard ../../wfg_interconnect/rtl/*.sv)
7 | SRC += $(wildcard ../../wfg_stim_sine/rtl/*.sv)
8 | SRC += $(wildcard ../../wfg_stim_mem/rtl/*.sv)
9 | SRC += $(wildcard ../../wfg_drive_spi/rtl/*.sv)
10 | SRC += $(wildcard ../../wfg_drive_pat/rtl/*.sv)
11 |
12 | # defaults
13 | SIM ?= icarus
14 | TOPLEVEL_LANG ?= verilog
15 |
16 | VERILOG_SOURCES += $(SRC)
17 |
18 | # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
19 | TOPLEVEL = wfg_top_tb
20 |
21 | # MODULE is the basename of the Python test file
22 | export PYTHONPATH := $(PYTHONPATH):../testbench/
23 | MODULE = test_wfg_top
24 |
25 | # include cocotb's make rules to take care of the simulator setup
26 | include $(shell cocotb-config --makefiles)/Makefile.sim
27 |
--------------------------------------------------------------------------------
/design/wfg_top/sim/memory.hex:
--------------------------------------------------------------------------------
1 | 00000000
2 | 00000001
3 | 00000002
4 | 00000003
5 | 00000004
6 | 00000005
7 | 00000006
8 | 00000007
9 | 00000008
10 | 00000009
11 | 0000000A
12 | 0000000B
13 | 0000000C
14 | 0000000D
15 | 0000000E
16 | 0000000F
17 | 00000010
18 | 00000011
19 | 00000012
20 | 00000013
21 | 00000014
22 | 00000015
23 | 00000016
24 | 00000017
25 | 00000018
26 | 00000019
27 | 0000001A
28 | 0000001B
29 | 0000001C
30 | 0000001D
31 | 0000001E
32 | 0000001F
33 |
--------------------------------------------------------------------------------
/design/wfg_top/sim/output_inc=1280_gain=16384_off=0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/design/wfg_top/sim/output_inc=1280_gain=16384_off=0.png
--------------------------------------------------------------------------------
/design/wfg_top/sim/output_inc=4096_gain=16384_off=0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/design/wfg_top/sim/output_inc=4096_gain=16384_off=0.png
--------------------------------------------------------------------------------
/design/wfg_top/sim/view.gtkw:
--------------------------------------------------------------------------------
1 | [*]
2 | [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI
3 | [*] Mon May 30 07:45:21 2022
4 | [*]
5 | [dumpfile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_top/sim/wfg_top_tb.vcd"
6 | [dumpfile_mtime] "Mon May 30 07:43:16 2022"
7 | [dumpfile_size] 992395
8 | [savefile] "/home/leo/Nextcloud/Fachhochschule/Bachelorarbeit/waveform-generator/design/wfg_top/sim/view.gtkw"
9 | [timestart] 0
10 | [size] 1920 1043
11 | [pos] -1971 -83
12 | *-24.000000 -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
13 | [treeopen] wfg_top_tb.
14 | [treeopen] wfg_top_tb.wfg_top.
15 | [treeopen] wfg_top_tb.wfg_top.wfg_stim_sine_top.
16 | [sst_width] 284
17 | [signals_width] 206
18 | [sst_expanded] 1
19 | [sst_vpaned_height] 295
20 | @28
21 | wfg_top_tb.io_wbs_ack
22 | @22
23 | wfg_top_tb.io_wbs_adr[31:0]
24 | @28
25 | wfg_top_tb.io_wbs_clk
26 | wfg_top_tb.io_wbs_cyc
27 | @22
28 | wfg_top_tb.io_wbs_datrd[31:0]
29 | wfg_top_tb.io_wbs_datwr[31:0]
30 | @28
31 | wfg_top_tb.io_wbs_rst
32 | wfg_top_tb.io_wbs_stb
33 | wfg_top_tb.io_wbs_we
34 | wfg_top_tb.wfg_drive_spi_cs_no
35 | wfg_top_tb.wfg_drive_spi_sclk_o
36 | wfg_top_tb.wfg_drive_spi_sdi_i
37 | wfg_top_tb.wfg_drive_spi_sdo_o
38 | @200
39 | -Stim Sine
40 | @29
41 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.clk
42 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.ctrl_en_q_i
43 | @421
44 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.cur_state
45 | @23
46 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.gain_val_q_i[15:0]
47 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.inc_val_q_i[15:0]
48 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.increment[15:0]
49 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.iteration[31:0]
50 | @421
51 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.next_state
52 | @23
53 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.offset_val_q_i[17:0]
54 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.overflow_chk[17:0]
55 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.phase_in[15:0]
56 | @29
57 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.quadrant[1:0]
58 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.rst_n
59 | @23
60 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.sin_17[16:0]
61 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.sin_18[17:0]
62 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.temp[34:0]
63 | @29
64 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.valid
65 | @23
66 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.wfg_axis_tdata_o[17:0]
67 | @29
68 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.wfg_axis_tready_i
69 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.wfg_axis_tvalid_o
70 | @23
71 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.x[16:0]
72 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.y[16:0]
73 | wfg_top_tb.wfg_top.wfg_stim_sine_top.wfg_stim_sine.z[16:0]
74 | [pattern_trace] 1
75 | [pattern_trace] 0
76 |
--------------------------------------------------------------------------------
/design/wfg_top/testbench/wfg_top_tb.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | `timescale 1ns / 1ps
5 |
6 | `ifdef VERILATOR // make parameter readable from VPI
7 | `define VL_RD /*verilator public_flat_rd*/
8 | `else
9 | `define VL_RD
10 | `endif
11 |
12 | module wfg_top_tb #(
13 | parameter int BUSW = 32
14 | ) (
15 | // Wishbone interface signals
16 | input io_wbs_clk,
17 | input io_wbs_rst,
18 | input [(BUSW-1):0] io_wbs_adr,
19 | input [(BUSW-1):0] io_wbs_datwr,
20 | output [(BUSW-1):0] io_wbs_datrd,
21 | input io_wbs_we,
22 | input io_wbs_stb,
23 | output io_wbs_ack,
24 | input io_wbs_cyc,
25 |
26 | input logic wfg_drive_spi_sdi_i, // for cocotb
27 | output logic wfg_drive_spi_sclk_o,
28 | output logic wfg_drive_spi_cs_no,
29 | output logic wfg_drive_spi_sdo_o,
30 |
31 | output logic [31:0] wfg_drive_pat_dout_o
32 | );
33 |
34 | logic csb1;
35 | logic [9:0 ] addr1;
36 | logic [31:0] dout1;
37 |
38 | localparam MEM_SIZE = 2 ** 10;
39 |
40 | logic [31:0] mem[MEM_SIZE];
41 |
42 | wfg_top wfg_top (
43 | .io_wbs_clk(io_wbs_clk),
44 | .io_wbs_rst(io_wbs_rst),
45 | .io_wbs_adr(io_wbs_adr),
46 | .io_wbs_datwr(io_wbs_datwr),
47 | .io_wbs_datrd(io_wbs_datrd),
48 | .io_wbs_we(io_wbs_we),
49 | .io_wbs_stb(io_wbs_stb),
50 | .io_wbs_ack(io_wbs_ack),
51 | .io_wbs_cyc(io_wbs_cyc),
52 |
53 | .wfg_drive_spi_sclk_o(wfg_drive_spi_sclk_o),
54 | .wfg_drive_spi_cs_no (wfg_drive_spi_cs_no),
55 | .wfg_drive_spi_sdo_o (wfg_drive_spi_sdo_o),
56 |
57 | .wfg_drive_pat_dout_o(wfg_drive_pat_dout_o),
58 |
59 | .csb1 (csb1),
60 | .addr1(addr1),
61 | .dout1(dout1)
62 | );
63 |
64 | initial begin
65 | $readmemh("memory.hex", mem);
66 | end
67 |
68 | always_ff @(negedge io_wbs_clk) begin
69 | if (!csb1) dout1 <= mem[addr1];
70 | end
71 |
72 | // Dump waves
73 | `ifndef VERILATOR
74 | initial begin
75 | $dumpfile("wfg_top_tb.vcd");
76 | $dumpvars(0, wfg_top_tb);
77 | end
78 | `endif
79 |
80 | endmodule
81 |
--------------------------------------------------------------------------------
/fpga/ulx3s_barebones/ulx3s_top.sv:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2022 semify
2 | // SPDX-License-Identifier: Apache-2.0
3 |
4 | module ulx3s_top (
5 | input clk_25mhz,
6 |
7 | input [6:0] btn,
8 | output logic [7:0] led
9 | );
10 | parameter int BUSW = 32;
11 |
12 | // Wishbone interface signals
13 | logic io_wbs_clk;
14 | logic io_wbs_rst;
15 | logic [(BUSW-1):0] io_wbs_adr;
16 | logic [(BUSW-1):0] io_wbs_datwr;
17 | logic [(BUSW-1):0] io_wbs_datrd;
18 | logic io_wbs_we;
19 | logic io_wbs_stb;
20 | logic io_wbs_ack;
21 | logic io_wbs_cyc;
22 |
23 | assign io_wbs_clk = clk_25mhz;
24 | assign io_wbs_rst = btn[0];
25 | assign io_wbs_adr = {btn[5], btn[4], btn[3], btn[2], btn[1], btn[0]};
26 | assign io_wbs_datwr = btn[2];
27 | assign io_wbs_we = btn[3];
28 | assign io_wbs_stb = btn[4];
29 | assign io_wbs_cyc = btn[5];
30 |
31 | assign led[3] = io_wbs_ack;
32 | assign led[4] = | io_wbs_datrd;
33 |
34 | logic wfg_drive_spi_sclk_o;
35 | logic wfg_drive_spi_cs_no;
36 | logic wfg_drive_spi_sdo_o;
37 | logic wfg_drive_spi_sdo_en_o;
38 |
39 | assign led[0] = wfg_drive_spi_sclk_o;
40 | assign led[1] = wfg_drive_spi_cs_no;
41 | assign led[2] = wfg_drive_spi_sdo_o;
42 |
43 | logic [31:0] wfg_drive_pat_dout_o;
44 |
45 | assign led[5] = | wfg_drive_pat_dout_o[7:0];
46 |
47 | // Memory interface
48 | logic csb1;
49 | logic [ 9:0] addr1;
50 | logic [31:0] dout1;
51 |
52 | localparam MEM_SIZE = 2 ** 10;
53 |
54 | logic [31:0] mem[MEM_SIZE];
55 |
56 | always_ff @(negedge io_wbs_clk) begin
57 | if (!csb1) dout1 <= mem[addr1];
58 | end
59 |
60 | (* keep *) wfg_top wfg_top (
61 | .io_wbs_clk(io_wbs_clk),
62 | .io_wbs_rst(io_wbs_rst),
63 | .io_wbs_adr(io_wbs_adr),
64 | .io_wbs_datwr(io_wbs_datwr),
65 | .io_wbs_datrd(io_wbs_datrd),
66 | .io_wbs_we(io_wbs_we),
67 | .io_wbs_stb(io_wbs_stb),
68 | .io_wbs_ack(io_wbs_ack),
69 | .io_wbs_cyc(io_wbs_cyc),
70 |
71 | .wfg_drive_spi_sclk_o(wfg_drive_spi_sclk_o),
72 | .wfg_drive_spi_cs_no(wfg_drive_spi_cs_no),
73 | .wfg_drive_spi_sdo_o(wfg_drive_spi_sdo_o),
74 |
75 | .wfg_drive_pat_dout_o(wfg_drive_pat_dout_o),
76 |
77 | .csb1(csb1),
78 | .addr1(addr1),
79 | .dout1(dout1)
80 | );
81 |
82 | endmodule
83 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/Makefile:
--------------------------------------------------------------------------------
1 | default: build
2 |
3 | #CPU_TYPE = vexriscv
4 | CPU_TYPE = femtorv
5 |
6 | firmware/firmware_sim.bin:
7 | cd firmware; make -f sim.make clean; make -f sim.make
8 |
9 | firmware/firmware_build.bin:
10 | cd firmware; make -f build.make clean; make -f build.make
11 |
12 | sim-prebuild:
13 | python3 ulx3s_soc.py --sim-debug --gtkwave-savefile --trace --trace-fst --with-wfg --simulate --cpu-type $(CPU_TYPE)
14 |
15 | sim: firmware/firmware_sim.bin
16 | python3 ulx3s_soc.py --rom-init=firmware/firmware_sim.bin --sim-debug --gtkwave-savefile --trace --trace-fst --with-wfg --simulate --cpu-type $(CPU_TYPE)
17 |
18 | build-prebuild:
19 | python3 ulx3s_soc.py --with-led-chaser --with-wfg --build --cpu-type $(CPU_TYPE)
20 |
21 | build: firmware/firmware_build.bin
22 | python3 ulx3s_soc.py --rom-init=firmware/firmware_build.bin --with-led-chaser --with-wfg --build --cpu-type $(CPU_TYPE)
23 |
24 | upload: build
25 | openFPGALoader --board=ulx3s build/radiona_ulx3s/gateware/radiona_ulx3s.bit
26 |
27 | only-upload:
28 | openFPGALoader --board=ulx3s build/radiona_ulx3s/gateware/radiona_ulx3s.bit
29 |
30 | view:
31 | gtkwave build/sim/gateware/sim.fst --save build/sim/gateware/sim.gtkw
32 |
33 | .PHONY: sim build upload only-upload view firmware/firmware_build.bin firmware/firmware_sim.bin
34 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/analyzer.csv:
--------------------------------------------------------------------------------
1 | config,None,data_width,204
2 | config,None,depth,512
3 | config,None,samplerate,1000000000000
4 | signal,0,ibus_stb,1
5 | signal,0,ibus_cyc,1
6 | signal,0,ibus_adr,30
7 | signal,0,ibus_we,1
8 | signal,0,ibus_ack,1
9 | signal,0,ibus_sel,4
10 | signal,0,ibus_dat_w,32
11 | signal,0,ibus_dat_r,32
12 | signal,0,dbus_stb,1
13 | signal,0,dbus_cyc,1
14 | signal,0,dbus_adr,30
15 | signal,0,dbus_we,1
16 | signal,0,dbus_ack,1
17 | signal,0,dbus_sel,4
18 | signal,0,dbus_dat_w,32
19 | signal,0,dbus_dat_r,32
20 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/csr.csv:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------------------
2 | # Auto-generated by LiteX (a977adf5) on 2022-07-18 10:34:45
3 | #--------------------------------------------------------------------------------
4 | csr_base,ctrl,0x82000000,,
5 | csr_base,identifier_mem,0x82000800,,
6 | csr_base,leds,0x82001000,,
7 | csr_base,timer0,0x82001800,,
8 | csr_base,uart,0x82002000,,
9 | csr_register,ctrl_reset,0x82000000,1,rw
10 | csr_register,ctrl_scratch,0x82000004,1,rw
11 | csr_register,ctrl_bus_errors,0x82000008,1,ro
12 | csr_register,leds_out,0x82001000,1,rw
13 | csr_register,timer0_load,0x82001800,1,rw
14 | csr_register,timer0_reload,0x82001804,1,rw
15 | csr_register,timer0_en,0x82001808,1,rw
16 | csr_register,timer0_update_value,0x8200180c,1,rw
17 | csr_register,timer0_value,0x82001810,1,ro
18 | csr_register,timer0_ev_status,0x82001814,1,ro
19 | csr_register,timer0_ev_pending,0x82001818,1,rw
20 | csr_register,timer0_ev_enable,0x8200181c,1,rw
21 | csr_register,uart_rxtx,0x82002000,1,rw
22 | csr_register,uart_txfull,0x82002004,1,ro
23 | csr_register,uart_rxempty,0x82002008,1,ro
24 | csr_register,uart_ev_status,0x8200200c,1,ro
25 | csr_register,uart_ev_pending,0x82002010,1,rw
26 | csr_register,uart_ev_enable,0x82002014,1,rw
27 | csr_register,uart_txempty,0x82002018,1,ro
28 | csr_register,uart_rxfull,0x8200201c,1,ro
29 | constant,config_clock_frequency,50000000,,
30 | constant,config_cpu_reset_addr,0,,
31 | constant,config_cpu_type_femtorv,None,,
32 | constant,config_cpu_variant_standard,None,,
33 | constant,config_cpu_human_name,femtorv-standard,,
34 | constant,config_cpu_nop,nop,,
35 | constant,config_rom_init,1,,
36 | constant,uart_polling,None,,
37 | constant,config_csr_data_width,32,,
38 | constant,config_csr_alignment,32,,
39 | constant,config_bus_standard,wishbone,,
40 | constant,config_bus_data_width,32,,
41 | constant,config_bus_address_width,32,,
42 | constant,config_bus_bursting,0,,
43 | memory_region,rom,0x00000000,131072,cached
44 | memory_region,sram,0x01000000,8192,cached
45 | memory_region,wfg,0x30000000,1048576,cached
46 | memory_region,csr,0x82000000,65536,io
47 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/build.make:
--------------------------------------------------------------------------------
1 | BUILD_DIR?=../build/radiona_ulx3s/
2 |
3 | include $(BUILD_DIR)/software/include/generated/variables.mak
4 | include $(SOC_DIRECTORY)/software/common.mak
5 |
6 | OBJECTS = sim_debug.o sim.o wfg.o donut.o crt0.o main.o
7 |
8 | all: firmware_build.bin
9 |
10 | %.bin: %.elf
11 | $(OBJCOPY) -O binary $< $@
12 | chmod -x $@
13 |
14 | vpath %.a $(PACKAGES:%=../%)
15 |
16 | firmware_build.elf: $(OBJECTS)
17 | $(CC) $(LDFLAGS) -T linker.ld -N -o $@ \
18 | $(OBJECTS) \
19 | $(PACKAGES:%=-L$(BUILD_DIR)/software/%) \
20 | -Wl,--gc-sections \
21 | $(LIBS:lib%=-l%)
22 | chmod -x $@
23 |
24 | # pull in dependency info for *existing* .o files
25 | -include $(OBJECTS:.o=.d)
26 |
27 | donut.o: CFLAGS += -w
28 |
29 | VPATH = $(BIOS_DIRECTORY):$(BIOS_DIRECTORY)/cmds:$(CPU_DIRECTORY)
30 |
31 | %.o: %.cpp
32 | $(compilexx)
33 |
34 | %.o: %.c
35 | $(compile)
36 |
37 | %.o: %.S
38 | $(assemble)
39 |
40 | clean:
41 | $(RM) $(OBJECTS) firmware_build.elf firmware_build.bin .*~ *~ *.d
42 |
43 | .PHONY: all clean
44 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/donut.c:
--------------------------------------------------------------------------------
1 | // The donut code with fixed-point arithmetic; no sines, cosines, square roots, or anything.
2 | // a1k0n 2020
3 | // Code from: https://gist.github.com/a1k0n/80f48aa8911fffd805316b8ba8f48e83
4 | // For more info:
5 | // - https://www.a1k0n.net/2011/07/20/donut-math.html
6 | // - https://www.youtube.com/watch?v=DEqXNfs_HhY
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include
13 |
14 | #define R(mul,shift,x,y) \
15 | _=x; \
16 | x -= mul*y>>shift; \
17 | y += mul*_>>shift; \
18 | _ = 3145728-x*x-y*y>>11; \
19 | x = x*_>>10; \
20 | y = y*_>>10;
21 |
22 | signed char b[1760], z[1760];
23 |
24 | void donut(void);
25 | void donut(void) {
26 | int sA=1024,cA=0,sB=1024,cB=0,_;
27 | for (;;) {
28 | memset(b, 32, 1760); // text buffer
29 | memset(z, 127, 1760); // z buffer
30 | int sj=0, cj=1024;
31 | for (int j = 0; j < 90; j++) {
32 | int si = 0, ci = 1024; // sine and cosine of angle i
33 | for (int i = 0; i < 324; i++) {
34 | int R1 = 1, R2 = 2048, K2 = 5120*1024;
35 |
36 | int x0 = R1*cj + R2,
37 | x1 = ci*x0 >> 10,
38 | x2 = cA*sj >> 10,
39 | x3 = si*x0 >> 10,
40 | x4 = R1*x2 - (sA*x3 >> 10),
41 | x5 = sA*sj >> 10,
42 | x6 = K2 + R1*1024*x5 + cA*x3,
43 | x7 = cj*si >> 10,
44 | x = 40 + 30*(cB*x1 - sB*x4)/x6,
45 | y = 12 + 15*(cB*x4 + sB*x1)/x6,
46 | N = (-cA*x7 - cB*((-sA*x7>>10) + x2) - ci*(cj*sB >> 10) >> 10) - x5 >> 7;
47 |
48 | int o = x + 80 * y;
49 | signed char zz = (x6-K2)>>15;
50 | if (22 > y && y > 0 && x > 0 && 80 > x && zz < z[o]) {
51 | z[o] = zz;
52 | b[o] = ".,-~:;=!*#$@"[N > 0 ? N : 0];
53 | }
54 | R(5, 8, ci, si) // rotate i
55 | }
56 | R(9, 7, cj, sj) // rotate j
57 | }
58 | for (int k = 0; 1761 > k; k++)
59 | putchar(k % 80 ? b[k] : 10);
60 | R(5, 7, cA, sA);
61 | R(5, 8, cB, sB);
62 | if (readchar_nonblock()) {
63 | getchar();
64 | break;
65 | }
66 | fputs("\x1b[23A", stdout);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/firmware_build.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/fpga/ulx3s_soc/firmware/firmware_build.bin
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/firmware_build.elf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/fpga/ulx3s_soc/firmware/firmware_build.elf
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/firmware_sim.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/fpga/ulx3s_soc/firmware/firmware_sim.bin
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/firmware_sim.elf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/fpga/ulx3s_soc/firmware/firmware_sim.elf
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/linker.ld:
--------------------------------------------------------------------------------
1 | INCLUDE generated/output_format.ld
2 | ENTRY(_start)
3 |
4 | __DYNAMIC = 0;
5 |
6 | INCLUDE generated/regions.ld
7 |
8 | SECTIONS
9 | {
10 | .text :
11 | {
12 | _ftext = .;
13 | /* Make sure crt0 files come first, and they, and the isr */
14 | /* don't get disposed of by greedy optimisation */
15 | *crt0*(.text)
16 | KEEP(*crt0*(.text))
17 | KEEP(*(.text.isr))
18 |
19 | *(.text .stub .text.* .gnu.linkonce.t.*)
20 | _etext = .;
21 | } > rom
22 |
23 | .rodata :
24 | {
25 | . = ALIGN(8);
26 | _frodata = .;
27 | *(.rodata .rodata.* .gnu.linkonce.r.*)
28 | *(.rodata1)
29 | . = ALIGN(8);
30 | _erodata = .;
31 | } > rom
32 |
33 | .data :
34 | {
35 | . = ALIGN(8);
36 | _fdata = .;
37 | *(.data .data.* .gnu.linkonce.d.*)
38 | *(.data1)
39 | _gp = ALIGN(16);
40 | *(.sdata .sdata.* .gnu.linkonce.s.*)
41 | . = ALIGN(8);
42 | _edata = .;
43 | } > sram AT > rom
44 |
45 | .bss :
46 | {
47 | . = ALIGN(8);
48 | _fbss = .;
49 | *(.dynsbss)
50 | *(.sbss .sbss.* .gnu.linkonce.sb.*)
51 | *(.scommon)
52 | *(.dynbss)
53 | *(.bss .bss.* .gnu.linkonce.b.*)
54 | *(COMMON)
55 | . = ALIGN(8);
56 | _ebss = .;
57 | _end = .;
58 | } > sram
59 | }
60 |
61 | PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram));
62 |
63 | PROVIDE(_fdata_rom = LOADADDR(.data));
64 | PROVIDE(_edata_rom = LOADADDR(.data) + SIZEOF(.data));
65 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/main.c:
--------------------------------------------------------------------------------
1 | // This file is Copyright (c) 2020 Florent Kermarrec
2 | // License: BSD
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | /*-----------------------------------------------------------------------*/
15 | /* Uart */
16 | /*-----------------------------------------------------------------------*/
17 |
18 | static char *readstr(void)
19 | {
20 | char c[2];
21 | static char s[64];
22 | static int ptr = 0;
23 |
24 | if(readchar_nonblock()) {
25 | c[0] = getchar();
26 | c[1] = 0;
27 | switch(c[0]) {
28 | case 0x7f:
29 | case 0x08:
30 | if(ptr > 0) {
31 | ptr--;
32 | fputs("\x08 \x08", stdout);
33 | }
34 | break;
35 | case 0x07:
36 | break;
37 | case '\r':
38 | case '\n':
39 | s[ptr] = 0x00;
40 | fputs("\n", stdout);
41 | ptr = 0;
42 | return s;
43 | default:
44 | if(ptr >= (sizeof(s) - 1))
45 | break;
46 | fputs(c, stdout);
47 | s[ptr] = c[0];
48 | ptr++;
49 | break;
50 | }
51 | }
52 |
53 | return NULL;
54 | }
55 |
56 | static char *get_token(char **str)
57 | {
58 | char *c, *d;
59 |
60 | c = (char *)strchr(*str, ' ');
61 | if(c == NULL) {
62 | d = *str;
63 | *str = *str+strlen(*str);
64 | return d;
65 | }
66 | *c = 0;
67 | d = *str;
68 | *str = c+1;
69 | return d;
70 | }
71 |
72 | static void prompt(void)
73 | {
74 | printf("\e[92;1msemify\e[0m> ");
75 | }
76 |
77 | /*-----------------------------------------------------------------------*/
78 | /* Help */
79 | /*-----------------------------------------------------------------------*/
80 |
81 | static void help(void)
82 | {
83 | puts("\nMy first LiteX SoC built "__DATE__" "__TIME__"\n");
84 | puts("Available commands:");
85 | puts("help - Show this command");
86 | puts("reboot - Reboot CPU");
87 | puts("donut - Spinning Donut demo");
88 | puts("mem_list - List available memory regions");
89 | puts("wfg_init - Initialize the waveform generator");
90 | #ifdef CSR_SIM_TRACE_BASE
91 | puts("trace - Toggle simulation tracing");
92 | #endif
93 | #ifdef CSR_SIM_FINISH_BASE
94 | puts("finish - Finish simulation");
95 | #endif
96 | #ifdef CSR_SIM_MARKER_BASE
97 | puts("mark - Set a debug simulation marker");
98 | #endif
99 | }
100 |
101 | /*-----------------------------------------------------------------------*/
102 | /* Commands */
103 | /*-----------------------------------------------------------------------*/
104 |
105 | static void reboot_cmd(void)
106 | {
107 | ctrl_reset_write(1);
108 | }
109 |
110 | extern void donut(void);
111 |
112 | static void donut_cmd(void)
113 | {
114 | printf("Donut demo...\n");
115 | donut();
116 | }
117 |
118 | static void mem_regions_cmd(void)
119 | {
120 | printf("Available memory regions:\n");
121 | puts(MEM_REGIONS);
122 | }
123 |
124 | #ifdef WFG_BASE
125 |
126 | extern void wfg_init(void);
127 | extern void wfg_inc_cnt(void);
128 | extern void wfg_dec_cnt(void);
129 |
130 | static void wfg_init_cmd(void)
131 | {
132 | printf("Initializing wfg...\n");
133 | wfg_init();
134 | }
135 |
136 | #endif
137 |
138 | #ifdef CSR_SIM_TRACE_BASE
139 | extern void cmd_sim_trace_handler(int nb_params, char **params);
140 | #endif
141 | #ifdef CSR_SIM_FINISH_BASE
142 | extern void cmd_sim_finish_handler(int nb_params, char **params);
143 | #endif
144 | #ifdef CSR_SIM_MARKER_BASE
145 | extern void cmd_sim_mark_handler(int nb_params, char **params);
146 | #endif
147 |
148 | /*-----------------------------------------------------------------------*/
149 | /* Console service / Main */
150 | /*-----------------------------------------------------------------------*/
151 |
152 | static void console_service(void)
153 | {
154 | char *str;
155 | char *token;
156 |
157 | str = readstr();
158 | if(str == NULL) return;
159 | token = get_token(&str);
160 | if(strcmp(token, "help") == 0)
161 | help();
162 | else if(strcmp(token, "reboot") == 0)
163 | reboot_cmd();
164 | else if(strcmp(token, "donut") == 0)
165 | donut_cmd();
166 | else if(strcmp(token, "mem_list") == 0)
167 | mem_regions_cmd();
168 | #ifdef CSR_SIM_TRACE_BASE
169 | else if(strcmp(token, "trace") == 0)
170 | cmd_sim_trace_handler(0, 0);
171 | #endif
172 | #ifdef CSR_SIM_FINISH_BASE
173 | else if(strcmp(token, "finish") == 0)
174 | cmd_sim_finish_handler(0, 0);
175 | #endif
176 | #ifdef CSR_SIM_MARKER_BASE
177 | else if(strcmp(token, "mark") == 0)
178 | cmd_sim_mark_handler(0, 0);
179 | #endif
180 | #ifdef WFG_BASE
181 | else if(strcmp(token, "wfg_init") == 0)
182 | wfg_init_cmd();
183 | else if(strcmp(token, "wfg_inc_cnt") == 0)
184 | wfg_inc_cnt();
185 | else if(strcmp(token, "wfg_dec_cnt") == 0)
186 | wfg_dec_cnt();
187 | #endif
188 | else
189 | printf("Unknown command. Type 'help' for help.\n");
190 | prompt();
191 | }
192 |
193 | int main(void)
194 | {
195 | #ifdef CONFIG_CPU_HAS_INTERRUPT
196 | irq_setmask(0);
197 | irq_setie(1);
198 | #endif
199 | uart_init();
200 |
201 | help();
202 | prompt();
203 |
204 | while(1) {
205 | console_service();
206 | }
207 |
208 | return 0;
209 | }
210 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/sim.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "sim_debug.h"
4 |
5 | /**
6 | * Command "trace"
7 | *
8 | * Start/stop simulation trace dump.
9 | *
10 | */
11 | #ifdef CSR_SIM_TRACE_BASE
12 | void cmd_sim_trace_handler(int nb_params, char **params)
13 | {
14 | sim_trace(!sim_trace_enable_read());
15 | }
16 | #endif
17 |
18 | /**
19 | * Command "finish"
20 | *
21 | * Finish simulation.
22 | *
23 | */
24 | #ifdef CSR_SIM_FINISH_BASE
25 | void cmd_sim_finish_handler(int nb_params, char **params)
26 | {
27 | sim_finish();
28 | }
29 | #endif
30 |
31 | /**
32 | * Command "mark"
33 | *
34 | * Set a debug marker value
35 | *
36 | */
37 | #ifdef CSR_SIM_MARKER_BASE
38 | void cmd_sim_mark_handler(int nb_params, char **params)
39 | {
40 | // cannot use param[1] as it is not a const string
41 | sim_mark(NULL);
42 | }
43 | #endif
44 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/sim.make:
--------------------------------------------------------------------------------
1 | BUILD_DIR?=../build/sim/
2 |
3 | include $(BUILD_DIR)/software/include/generated/variables.mak
4 | include $(SOC_DIRECTORY)/software/common.mak
5 |
6 | OBJECTS = sim_debug.o sim.o wfg.o donut.o crt0.o main.o
7 |
8 | all: firmware_sim.bin
9 |
10 | %.bin: %.elf
11 | $(OBJCOPY) -O binary $< $@
12 | chmod -x $@
13 |
14 | vpath %.a $(PACKAGES:%=../%)
15 |
16 | firmware_sim.elf: $(OBJECTS)
17 | $(CC) $(LDFLAGS) -T linker.ld -N -o $@ \
18 | $(OBJECTS) \
19 | $(PACKAGES:%=-L$(BUILD_DIR)/software/%) \
20 | -Wl,--gc-sections \
21 | $(LIBS:lib%=-l%)
22 | chmod -x $@
23 |
24 | # pull in dependency info for *existing* .o files
25 | -include $(OBJECTS:.o=.d)
26 |
27 | donut.o: CFLAGS += -w
28 |
29 | VPATH = $(BIOS_DIRECTORY):$(BIOS_DIRECTORY)/cmds:$(CPU_DIRECTORY)
30 |
31 | %.o: %.cpp
32 | $(compilexx)
33 |
34 | %.o: %.c
35 | $(compile)
36 |
37 | %.o: %.S
38 | $(assemble)
39 |
40 | clean:
41 | $(RM) $(OBJECTS) firmware_sim.elf firmware_sim.bin .*~ *~ *.d
42 |
43 | .PHONY: all clean
44 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/sim_debug.c:
--------------------------------------------------------------------------------
1 | #include "sim_debug.h"
2 |
3 | #include
4 | #include
5 |
6 | // 0 is used as no marker
7 | #define MAX_N_MARKERS (255 - 1)
8 |
9 | #ifdef CSR_SIM_MARKER_BASE
10 | static int n_markers = 0;
11 | static const char *markers[MAX_N_MARKERS] = {0};
12 | #endif
13 |
14 | void sim_mark(const char *text) {
15 | #ifdef CSR_SIM_MARKER_BASE
16 | if (text == NULL) {
17 | text = "NO COMMENT";
18 | }
19 | // 0 is not used
20 | int marker_num = n_markers + 1;
21 | markers[n_markers++] = text;
22 | sim_marker_marker_write(marker_num);
23 | if (n_markers >= MAX_N_MARKERS) {
24 | printf("Max number of markers reached\n");
25 | n_markers = 0;
26 | }
27 | #else
28 | printf("No sim_marker CSR\n");
29 | #endif
30 | }
31 |
32 | void sim_markers_summary(void) {
33 | #ifdef CSR_SIM_MARKER_BASE
34 | printf("\nMarkers:\n");
35 | for (int i = 0; i < n_markers; ++i) {
36 | printf(" %3d: %s\n", i + 1, markers[i]);
37 | }
38 | printf("\n");
39 | #else
40 | printf("No sim_marker CSR\n");
41 | #endif
42 | }
43 |
44 | void sim_trace(int on) {
45 | #ifdef CSR_SIM_TRACE_BASE
46 | sim_trace_enable_write(on);
47 | #else
48 | printf("No sim_trace CSR\n");
49 | #endif
50 | }
51 |
52 | int sim_trace_on(void) {
53 | #ifdef CSR_SIM_TRACE_BASE
54 | return sim_trace_enable_read();
55 | #else
56 | printf("No sim_trace CSR\n");
57 | return 0;
58 | #endif
59 | }
60 |
61 | void sim_finish(void) {
62 | #ifdef CSR_SIM_FINISH_BASE
63 | sim_trace(0);
64 | if (n_markers > 0) {
65 | sim_markers_summary();
66 | }
67 | sim_finish_finish_write(1);
68 | #else
69 | printf("No sim_finish CSR\n");
70 | #endif
71 | }
72 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/sim_debug.h:
--------------------------------------------------------------------------------
1 | #ifndef __SIM_DEBUG_H
2 | #define __SIM_DEBUG_H
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | // add next marker with given comment
9 | void sim_mark(const char *comment);
10 | #define sim_mark_func() sim_mark(__func__)
11 | // print the summary of markers mapping (number -> comment)
12 | void sim_markers_summary(void);
13 | // enable simulation trace dump
14 | void sim_trace(int on);
15 | // check if trace is on
16 | int sim_trace_on(void);
17 | // finish simulation
18 | void sim_finish(void);
19 |
20 | #ifdef __cplusplus
21 | }
22 | #endif
23 |
24 | #endif
25 |
26 |
--------------------------------------------------------------------------------
/fpga/ulx3s_soc/firmware/wfg.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #ifdef WFG_BASE
5 |
6 | int cnt = 3;
7 | int cpol = 0;
8 | int lsbfirst = 0;
9 | int dff = 3;
10 | int sspol = 0;
11 |
12 | /*
13 | Write to a register of the waveform generator
14 |
15 | peripheral:
16 | - core - 0x01
17 | - interconnect - 0x02
18 | - stim_sine - 0x03
19 | - stim_mem - 0x04
20 | - drive_spi - 0x05
21 | - drive_pat - 0x06
22 |
23 | address:
24 | The address of the register, can be 0-15
25 | */
26 | void wfg_set_register(int peripheral, int address, int value)
27 | {
28 | *(volatile int*)(WFG_BASE + (peripheral<<4) + (address & 0xF)) = value;
29 |
30 | int readback = *(volatile int*)(WFG_BASE + (peripheral<<4) + (address & 0xF));
31 |
32 | if (readback != value)
33 | {
34 | printf("Wrong value: %d != %d\n", readback, value);
35 | }
36 | }
37 |
38 | void wfg_inc_cnt(void)
39 | {
40 | cnt++;
41 | printf("cnt: %d\n", cnt);
42 | wfg_set_register(0x3, 0x8, cnt); // Clock divider
43 | }
44 |
45 | void wfg_dec_cnt(void)
46 | {
47 | cnt--;
48 | printf("cnt: %d\n", cnt);
49 | wfg_set_register(0x3, 0x8, cnt); // Clock divider
50 | }
51 |
52 | void wfg_init(void)
53 | {
54 | //*(volatile int*)(WFG_BASE) = 0xDEADBEEF;
55 |
56 | int core_sync_count = 16;
57 | int core_subcycle_count = 16;
58 |
59 | int subcore_sync_count = 32;
60 | int subcore_subcycle_count = 16;
61 |
62 | // Core
63 | wfg_set_register(0x1, 0x4, (core_sync_count << 0) | (core_subcycle_count << 8));
64 | wfg_set_register(0x1, 0x0, 1); // Enable
65 |
66 | // Subcore
67 | wfg_set_register(0x2, 0x4, (subcore_sync_count << 0) | (subcore_subcycle_count << 8));
68 | wfg_set_register(0x2, 0x0, 1); // Enable
69 |
70 | // Interconnect
71 | wfg_set_register(0x3, 0x4, 0); // Driver0
72 | wfg_set_register(0x3, 0x8, 1); // Driver1
73 | wfg_set_register(0x3, 0x0, 1); // Enable
74 |
75 | // Sine
76 | wfg_set_register(0x4, 0x0, 1); // Enable
77 |
78 | // Mem
79 | wfg_set_register(0x5, 0x4, 0x4); // Start
80 | wfg_set_register(0x5, 0x8, 0xF); // End
81 | wfg_set_register(0x5, 0xC, 0x2); // Increment
82 | wfg_set_register(0x5, 0x0, 1); // Enable
83 |
84 | // SPI
85 | wfg_set_register(0x6, 0x8, cnt); // Clock divider
86 | wfg_set_register(0x6, 0x4, (cpol<<0) | (lsbfirst<<1) | (dff<<2) | (sspol<<4));
87 | wfg_set_register(0x6, 0x0, 1); // Enable SPI
88 |
89 | // Pattern
90 | wfg_set_register(0x7, 0x4, (0) | (8<<8) ); // Start:End
91 | wfg_set_register(0x7, 0x8, 0xFFFFFFFF); // Low bit
92 | wfg_set_register(0x7, 0xC, 0xFFFFFFFF); // High bit
93 | wfg_set_register(0x7, 0x0, 0xFFFFFFFF); // Enable all bits
94 | }
95 |
96 | #endif
97 |
--------------------------------------------------------------------------------
/img/WFG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/img/WFG.png
--------------------------------------------------------------------------------
/img/litex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/img/litex.png
--------------------------------------------------------------------------------
/img/utilization.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semify-eda/waveform-generator/3afa6c9c8eb7268c48bfb7fba8a825d9a69643f2/img/utilization.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | cocotb==1.6.2
2 | cocotb-bus==0.2.1
3 | cocotbext-axi==0.1.18
4 | cocotbext-spi==0.2.0
5 | cocotbext-wishbone==0.2.2
6 | Jinja2==3.1.2
7 | matplotlib==3.5.2
8 | numpy==1.22.3
9 | pytest==7.1.2
10 | scipy==1.8.0
11 |
--------------------------------------------------------------------------------
/templating/converter.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # SPDX-FileCopyrightText: © 2022 semify
3 | # SPDX-License-Identifier: Apache-2.0
4 |
5 | import os
6 | import sys
7 | import csv
8 | import json
9 | import argparse
10 | import pathlib
11 |
12 | def dir_path(string):
13 | path = pathlib.Path(string)
14 |
15 | if not path.exists():
16 | raise argparse.ArgumentTypeError(f"{path} does not exist")
17 |
18 | if not path.is_dir():
19 | raise argparse.ArgumentTypeError(f"{path} is not a directory")
20 |
21 | return string
22 |
23 | def file_path(string):
24 | path = pathlib.Path(string)
25 |
26 | if not path.exists():
27 | raise argparse.ArgumentTypeError(f"{path} does not exist")
28 |
29 | if not path.is_file():
30 | raise argparse.ArgumentTypeError(f"{path} is not a file")
31 |
32 | return string
33 |
34 | def main():
35 | parser = argparse.ArgumentParser(description='Generate code blocks from templates inside files.')
36 | parser.add_argument('-i', '--input', type=file_path, nargs='+', required=True, help='input file')
37 | parser.add_argument('-o', '--output', type=str, nargs='+', help='output file, if not set equals input file')
38 | parser.add_argument('-v', '--verbose', action='store_true', help='verbose output')
39 |
40 | args = parser.parse_args()
41 |
42 | # Set file names
43 | in_filenames = [pathlib.Path(input) for input in args.input]
44 | out_filenames = [pathlib.Path(output) for output in args.output]
45 |
46 | if len(in_filenames) != len(out_filenames):
47 | print('Error: The number of input and output files must be equal')
48 | sys.exit(0)
49 |
50 | for (file_cnt, in_filename) in enumerate(in_filenames):
51 | out_filename = out_filenames[file_cnt]
52 |
53 | if in_filename.suffix != ".csv":
54 | print("Currently only .csv files can be converted")
55 | sys.exit(0)
56 |
57 | if args.verbose:
58 | print("in_filename: {}".format(in_filename))
59 | print("out_filename: {}".format(out_filename))
60 |
61 | json_dict = {"registers" : {}}
62 |
63 | with open(in_filename, 'r') as f_csv:
64 | csv_reader = csv.DictReader(f_csv)
65 |
66 | last_register = None
67 |
68 | for entry in csv_reader:
69 | if args.verbose:
70 | print("Current entry: {}".format(entry))
71 |
72 | # Register
73 | if entry['RegName'] != '':
74 | last_register = entry['RegName']
75 | json_dict["registers"][entry['RegName']] = {"address" : entry['Address'],
76 | "description" : entry['Description'],
77 | "entries" : {}
78 | }
79 | else:
80 | json_dict["registers"][last_register]["entries"][entry['BitName']] = {"access" : entry['Access'],
81 | "hardware" : entry['HW'],
82 | "LSB" : entry['LSB'],
83 | "MSB" : entry['MSB'],
84 | "reset" : entry['Reset'],
85 | "description": entry['Description']
86 | }
87 |
88 | if args.verbose:
89 | print("Complete dictionary:")
90 | print(json_dict)
91 |
92 | with open(out_filename, "w") as f_json:
93 | json.dump(json_dict, f_json, indent=4, sort_keys=True)
94 |
95 | print("Library conversion done.")
96 |
97 | if __name__ == "__main__":
98 | main()
99 |
--------------------------------------------------------------------------------
/templating/templates/wishbone/assign_from_registers.template:
--------------------------------------------------------------------------------
1 |
2 | {% for register in registers -%}
3 | {%- set outer_loop = loop -%}
4 |
5 | {{'{:<12}'.format(registers[register].address + ':')}}
6 |
7 | {%- if registers[register].entries|length > 1 -%}
8 | begin
9 | {% endif -%}
10 |
11 | {%- for entry in registers[register].entries -%}
12 | {%- set current_entry = registers[register].entries[entry] -%}
13 |
14 | {%- if registers[register].entries|length > 1 -%}
15 | {{'{:<12}wbs_dat_o[{:>2}:{:>2}] <= {};'.format('', current_entry.MSB, current_entry.LSB, register.lower() + '_' + entry.lower() + '_ff')}}
16 | {% else -%}
17 | {{'wbs_dat_o[{:>2}:{:>2}] <= {};'.format(current_entry.MSB, current_entry.LSB, register.lower() + '_' + entry.lower() + '_ff')}}
18 | {% endif -%}
19 | {%- endfor -%}
20 |
21 | {%- if registers[register].entries|length > 1 -%}
22 | end
23 | {% endif -%}
24 |
25 | {%- endfor %}
26 |
--------------------------------------------------------------------------------
/templating/templates/wishbone/assign_outputs.template:
--------------------------------------------------------------------------------
1 |
2 | {% for register in registers -%}
3 | {%- set outer_loop = loop -%}
4 | {%- for entry in registers[register].entries -%}
5 | {%- set current_entry = registers[register].entries[entry] -%}
6 | {{ 'assign {:<24} = {};'.format(register.lower()+'_'+entry.lower()+'_q_o', register.lower() + '_' + entry.lower() + '_ff') }}
7 | {% endfor -%}
8 | {%- endfor %}
9 |
--------------------------------------------------------------------------------
/templating/templates/wishbone/assign_to_module.template:
--------------------------------------------------------------------------------
1 |
2 | {% for register in registers -%}
3 | {%- set outer_loop = loop -%}
4 | {%- for entry in registers[register].entries -%}
5 | {%- set inner_loop = loop -%}
6 | {%- set current_entry = registers[register].entries[entry] -%}
7 |
8 | {%- if current_entry.hardware == 'cfg' -%}
9 |
10 | {#- Check if there is another cfg register after me -#}
11 |
12 | {%- set ns = namespace(iamlast=true, foundme=false) -%}
13 |
14 | {% for other_register in registers -%}
15 | {%- set other_outer_loop = loop -%}
16 | {%- for other_entry in registers[other_register].entries -%}
17 | {%- set other_inner_loop = loop -%}
18 |
19 | {%- if ns.foundme == true -%}
20 | {%- if registers[other_register].entries[other_entry].hardware == 'cfg' -%}
21 | {%- set ns.iamlast = false -%}
22 | {%- endif -%}
23 | {%- endif -%}
24 |
25 | {%- if register == other_register and other_entry == entry -%}
26 | {%- set ns.foundme = true -%}
27 | {%- endif -%}
28 | {%- endfor -%}
29 | {%- endfor -%}
30 |
31 | {%- set delimiter = ',' -%}
32 | {%- if ns.iamlast == 1 -%}
33 | {%- set delimiter = '' -%}
34 | {%- endif -%}
35 |
36 | {{ '.{:<24}({:<24}){:<2}// {}'.format(register.lower()+'_'+entry.lower()+'_q_o', register.lower()+'_'+entry.lower()+'_q', delimiter, register + '.' + entry + ' register output') }}
37 | {% endif -%}
38 | {%- endfor -%}
39 | {%- endfor %}
40 |
--------------------------------------------------------------------------------
/templating/templates/wishbone/assign_to_registers.template:
--------------------------------------------------------------------------------
1 |
2 | {% for register in registers -%}
3 | {%- set outer_loop = loop -%}
4 |
5 | {{'{:<12}'.format(registers[register].address + ':')}}
6 |
7 | {%- if registers[register].entries|length > 1 -%}
8 | begin
9 | {% endif -%}
10 |
11 | {%- for entry in registers[register].entries -%}
12 | {%- set current_entry = registers[register].entries[entry] -%}
13 |
14 | {%- if registers[register].entries|length > 1 -%}
15 | {{'{:<12}{:<24} <= wbs_dat_i[{:>2}:{:>2}];'.format('', register.lower() + '_' + entry.lower() + '_ff', current_entry.MSB, current_entry.LSB)}}
16 | {% else -%}
17 | {{'{:<24} <= wbs_dat_i[{:>2}:{:>2}];'.format(register.lower() + '_' + entry.lower() + '_ff', current_entry.MSB, current_entry.LSB)}}
18 | {% endif -%}
19 | {%- endfor -%}
20 |
21 |
22 | {%- if registers[register].entries|length > 1 -%}
23 | end
24 | {% endif -%}
25 |
26 | {%- endfor %}
27 |
--------------------------------------------------------------------------------
/templating/templates/wishbone/instantiate_registers.template:
--------------------------------------------------------------------------------
1 |
2 | {% for register in registers -%}
3 | {%- set outer_loop = loop -%}
4 | {%- for entry in registers[register].entries -%}
5 | {%- set current_entry = registers[register].entries[entry] -%}
6 | {%- if current_entry.MSB == current_entry.LSB -%}
7 | {%- set name = register.lower()+"_"+entry.lower()+"_ff;" -%}
8 | {{ 'logic {:<24} // {}'.format(register.lower()+'_'+entry.lower()+'_ff;', register + '.' + entry + ' FF') }}
9 | {% else -%}
10 | {%- set name = "["+current_entry.MSB+":"+current_entry.LSB+"] "+register.lower()+"_"+entry.lower()+"_ff;" -%}
11 | {{ 'logic [{:>2}:{:>2}] {:<24} // {}'.format(current_entry.MSB, current_entry.LSB, register.lower()+'_'+entry.lower()+'_ff;', register + '.' + entry + ' FF') }}
12 | {% endif -%}
13 | {%- endfor -%}
14 | {% endfor %}
15 |
--------------------------------------------------------------------------------
/templating/templates/wishbone/instantiate_top.template:
--------------------------------------------------------------------------------
1 |
2 | {% for register in registers -%}
3 | {%- set outer_loop = loop -%}
4 | {%- for entry in registers[register].entries -%}
5 | {%- set current_entry = registers[register].entries[entry] -%}
6 | {%- if current_entry.MSB == current_entry.LSB -%}
7 | {%- set name = register.lower()+"_"+entry.lower()+"_ff;" -%}
8 | {{ 'logic {:<24} // {}'.format(register.lower()+'_'+entry.lower()+'_q;', register + '.' + entry + ' register output') }}
9 | {% else -%}
10 | {%- set name = "["+current_entry.MSB+":"+current_entry.LSB+"] "+register.lower()+"_"+entry.lower()+"_ff;" -%}
11 | {{ 'logic [{:>2}:{:>2}] {:<24} // {}'.format(current_entry.MSB, current_entry.LSB, register.lower()+'_'+entry.lower()+'_q;', register + '.' + entry + ' register output') }}
12 | {% endif -%}
13 | {%- endfor -%}
14 | {% endfor %}
15 |
--------------------------------------------------------------------------------
/templating/templates/wishbone/register_interface.template:
--------------------------------------------------------------------------------
1 |
2 | {% for register in registers -%}
3 | {%- set outer_loop = loop -%}
4 | {%- for entry in registers[register].entries -%}
5 | {%- set current_entry = registers[register].entries[entry] -%}
6 |
7 | {%- set delimiter = ',' -%}
8 | {%- if outer_loop.last and loop.last -%}
9 | {%- set delimiter = '' -%}
10 | {%- endif -%}
11 |
12 | {%- if current_entry.MSB == current_entry.LSB -%}
13 | {{ 'output logic {:<24} // {}'.format(register.lower()+'_'+entry.lower()+'_q_o'+delimiter, register + '.' + entry + ' register output') }}
14 | {% else -%}
15 | {{ 'output logic [{:>2}:{:>2}] {:<24} // {}'.format(current_entry.MSB, current_entry.LSB, register.lower()+'_'+entry.lower()+'_q_o'+delimiter, register + '.' + entry + ' register output') }}
16 | {% endif -%}
17 | {%- endfor -%}
18 | {%- endfor %}
19 |
--------------------------------------------------------------------------------
/templating/templates/wishbone/register_overview.template:
--------------------------------------------------------------------------------
1 |
2 | {% for register in registers -%}
3 | {%- set address_int = register.address | int(base=16) -%}
4 | {%- set bits_int = register.bits | int -%}
5 | // 0x{{ '%0x' % address_int }} : Data signal of {{ register.name }}
6 | // bit {{ bits_int - 1 }}~0 - {{ register.name }}[{{ bits_int - 1 }}:0] (Read/Write)
7 | // others - reserved
8 | {% endfor -%}
9 |
--------------------------------------------------------------------------------
/templating/templates/wishbone/reset_registers.template:
--------------------------------------------------------------------------------
1 |
2 | {% for register in registers -%}
3 | {%- set outer_loop = loop -%}
4 | {%- for entry in registers[register].entries -%}
5 | {%- set current_entry = registers[register].entries[entry] -%}
6 | {{ '%-24s <= %s;' | format(register.lower() + '_' + entry.lower() + '_ff', current_entry.reset) }}
7 | {% endfor -%}
8 | {%- endfor %}
9 |
--------------------------------------------------------------------------------