diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5c810def1..388ac139c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,10 +18,10 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: '3.8' @@ -49,13 +49,13 @@ jobs: make -C doc html - name: Deploy gh-pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./doc/_build/html - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: badger-doc path: badger/doc/*.svg diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6157d894f..8f6fa639d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,8 +8,7 @@ stages: variables: XILINX_VIVADO: /non-free/Xilinx/Vivado/2020.2 CI_REGISTRY: mohs.dhcp.lbl.gov - CONTAINER_IMAGE: $CI_REGISTRY/testing_base_bookworm - # CONTAINER_IM_IMAGE: $CI_REGISTRY/riscv_bloat + CONTAINER_IMAGE: $CI_REGISTRY/bedrock_testing_base_bookworm DOCKER_HOST: tcp://docker:2375/ DOCKER_DRIVER: overlay2 @@ -19,7 +18,6 @@ services: - name: mohs.dhcp.lbl.gov/docker:20.10.12-dind command: ["--insecure-registry", "mohs.dhcp.lbl.gov"] alias: docker -# entrypoint: ["dockerd-entrypoint.sh"] include: - local: .gitlab/ci/badger.gitlab-ci.yml @@ -38,11 +36,12 @@ include: - local: .gitlab/ci/cdc_check.gitlab-ci.yml - local: .gitlab/ci/localbus.gitlab-ci.yml - local: .gitlab/ci/ctrace.gitlab-ci.yml + - local: .gitlab/ci/leep.gitlab-ci.yml - local: .gitlab/ci/axi.gitlab-ci.yml leep_test: script: - - cd projects/common && python3 -m unittest -v + - cd projects/common && PYTHONPATH=../../build-tools python3 -m unittest -v flake8: stage: test diff --git a/.gitlab/ci/badger.gitlab-ci.yml b/.gitlab/ci/badger.gitlab-ci.yml index 09e881e53..ae5cbd874 100644 --- a/.gitlab/ci/badger.gitlab-ci.yml +++ b/.gitlab/ci/badger.gitlab-ci.yml @@ -25,4 +25,4 @@ badger_ac701_run: dependencies: - badger_ac701 script: - - cd badger/tests && test -r ac701_rgmii_vtest.bit && sh teststand_ac701.sh + - cd badger/tests && test -r ac701_rgmii_vtest.bit && SERIAL_NUM_OPT="-s 210203356870" sh teststand_ac701.sh diff --git a/.gitlab/ci/build.gitlab-ci.yml b/.gitlab/ci/build.gitlab-ci.yml index e801342f4..91fc1f723 100644 --- a/.gitlab/ci/build.gitlab-ci.yml +++ b/.gitlab/ci/build.gitlab-ci.yml @@ -15,6 +15,7 @@ build: -t $CONTAINER_IMAGE:$CI_PROJECT_NAME-$CI_COMMIT_SHORT_SHA \ -t $CONTAINER_IMAGE:latest \ . + docker run --rm $CONTAINER_IMAGE bash -c 'echo -n "Debian version: "; cat "/etc/debian_version"' docker push $CONTAINER_IMAGE:$CI_COMMIT_REF_NAME docker push $CONTAINER_IMAGE:$CI_PROJECT_NAME-$CI_COMMIT_SHORT_SHA docker push $CONTAINER_IMAGE:latest diff --git a/.gitlab/ci/comms_top.gitlab-ci.yml b/.gitlab/ci/comms_top.gitlab-ci.yml index 47f124952..1c87bb98a 100644 --- a/.gitlab/ci/comms_top.gitlab-ci.yml +++ b/.gitlab/ci/comms_top.gitlab-ci.yml @@ -40,4 +40,4 @@ gige_sfp_ac701_run: dependencies: - gige_sfp_ac701 script: - - cd projects/comms_top/gige_eth && make hwload_ac701 && make hwtest_ac701 + - cd projects/comms_top/gige_eth && make hwload_ac701 SERIAL_NUM_OPT="-s 210203356870" && sleep 7 && make hwtest_ac701 diff --git a/.gitlab/ci/leep.gitlab-ci.yml b/.gitlab/ci/leep.gitlab-ci.yml new file mode 100644 index 000000000..856356430 --- /dev/null +++ b/.gitlab/ci/leep.gitlab-ci.yml @@ -0,0 +1,4 @@ +leep_test2: + stage: test + script: + - make -C projects/common/leep && make -C projects/common/leep clean diff --git a/.gitlab/ci/oscope.gitlab-ci.yml b/.gitlab/ci/oscope.gitlab-ci.yml index 854e258dd..a3885ee10 100644 --- a/.gitlab/ci/oscope.gitlab-ci.yml +++ b/.gitlab/ci/oscope.gitlab-ci.yml @@ -5,6 +5,20 @@ oscope_top_test: script: - make Voscope_top_tb && make Voscope_top_leep && make clean +# We know the design doesn't yet get evaluated as CDC-clean, at least in part +# due to Verilog inout ports being poorly supported by our tools. +# Please take out the error bypass (echo) if this ever gets fixed. +oscope_cdc: + before_script: + - cd projects/oscope/marble_family + stage: test + script: + - make dep && make oscope_prep_yosys.json && (make oscope_prep_cdc.txt || echo "Found CDC violation, as expected; continuing") + artifacts: + expire_in: 1 week + paths: + - projects/oscope/marble_family/oscope_prep_cdc.txt + oscope_top_bmb7: before_script: - cd projects/oscope/bmb7_cu && ls /non-free @@ -29,17 +43,15 @@ oscope_top_marble: paths: - projects/oscope/marble_family/oscope_top.bit +# LITEX_INSTALL_PATH is defined in the Docker image marble_ddr3_test: stage: synthesis before_script: - - apt-get update && apt-get install -y ninja-build && pip3 install meson==0.64.1 - - mkdir /litex_setup_dir - - (BD=$PWD && cd /litex_setup_dir && sh $BD/build-tools/litex_meta.sh) - - cd /litex_setup_dir/litex-boards/litex_boards/targets + - cd $LITEX_INSTALL_PATH/litex-boards/litex_boards/targets script: - XILINXD_LICENSE_FILE=$XILINXD_LICENSE_FILE PATH=$XILINX_VIVADO/bin:$PATH && python3 berkeleylab_marble.py --build - echo $CI_PROJECT_DIR - - cp /litex_setup_dir/litex-boards/litex_boards/targets/build/berkeleylab_marble/gateware/berkeleylab_marble.bit $CI_PROJECT_DIR/ + - cp $LITEX_INSTALL_PATH/litex-boards/litex_boards/targets/build/berkeleylab_marble/gateware/berkeleylab_marble.bit $CI_PROJECT_DIR/ artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME" expire_in: 1 week @@ -50,9 +62,6 @@ marble_ddr3_test: litex_trigger_capture: stage: synthesis before_script: - - apt-get update && apt-get install -y ninja-build && pip3 install meson==0.64.1 - - mkdir /litex_setup_dir - - (BD=$PWD && cd /litex_setup_dir && sh $BD/build-tools/litex_meta.sh) - cd projects/trigger_capture script: XILINXD_LICENSE_FILE=$XILINXD_LICENSE_FILE PATH=$XILINX_VIVADO/bin:$PATH && make marble.bit diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6022a46d5..27d093dd5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,7 +105,7 @@ a git commit ID (or similar) instead. PDF and object files often take extra effort to make reproducible. FPGA bitfiles are not typically shown to be reproducible, but Xilinx -[acknowledges](https://support.xilinx.com/s/article/61599) that is a +[acknowledges](https://adaptivesupport.amd.com/s/article/61599?language=en_US) that is a reasonable goal (they call it "repeatable"). Our toolchain has demonstrated this working in at least couple of cases. @@ -121,7 +121,7 @@ We're not the first group to deal with this topic. The following are useful resources: 1. [Linux kernel coding style guide](https://www.kernel.org/doc/html/v4.10/process/coding-style.html) many of the concepts it discusses have general applicability. -2. [Google python guide](http://google.github.io/styleguide/pyguide.html) and [PEP8](https://www.python.org/dev/peps/pep-0008/) +2. [Google python guide](https://google.github.io/styleguide/pyguide.html) and [PEP8](https://www.python.org/dev/peps/pep-0008/) 3. [GNU Coding Standards: Makefile Conventions](https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html) Please spell-check your code, comments, and documentation. @@ -164,7 +164,7 @@ to represent logical indentation level has advantages; people's setting of tab width can systematically and locally adjust the visual indentation level. Just like Wikipedia's English vs. American spelling policy, don't gratuitously change the tab vs. spaces convention of a file. See also -[Silicon Valley - S03E06 - Tabs versus Spaces](https://www.youtube.com/watch?v=SsoOG6ZeyUI) (2:50). +[Silicon Valley - S03E06 - Tabs versus Spaces](https://www.youtube.com/watch?v=cowtgmZuai0) (2:01). Special case for python files: No tabs, per [PEP8](https://www.python.org/dev/peps/pep-0008/) diff --git a/Dockerfile b/Dockerfile index af52d593b..99e2ac481 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bookworm-slim as testing_base_bookworm +FROM debian:12.8-slim AS testing_base_bookworm # Vivado needs libtinfo5, at least for Artix? RUN apt-get update && \ @@ -114,3 +114,33 @@ RUN apt-get update && \ libidn12 && \ rm -rf /var/lib/apt/lists/* && \ ln -s libidn.so.12 /usr/lib/x86_64-linux-gnu/libidn.so.11 + +# Install litex +RUN apt-get update && \ + apt-get install -y \ + ninja-build \ + gcc-aarch64-linux-gnu \ + ghdl && \ + rm -rf /var/lib/apt/lists/* && \ + pip3 install \ + meson + +COPY build-tools/litex_meta.sh / + +ENV LITEX_INSTALL_PATH=/litex + +RUN mkdir ${LITEX_INSTALL_PATH} && \ + cd ${LITEX_INSTALL_PATH} && \ + sh /litex_meta.sh + +# Install sv2v +RUN apt-get update && \ + apt-get install -y \ + haskell-stack && \ + rm -rf /var/lib/apt/lists/* && \ + git clone https://github.com/zachjs/sv2v /sv2v && \ + cd /sv2v && \ + git checkout 7808819c48c167978aeb5ef34c6e5ed416e90875 && \ + make && \ + rm -rf $HOME/.stack && \ + cp bin/sv2v /usr/local/bin/ diff --git a/README.md b/README.md index dd6eee41f..d1e1355aa 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Overview ======== -Bedrock generated documentation: https://berkeleylab.github.io/Bedrock +Bedrock generated documentation: https://berkeleylab.github.io/Bedrock/ [Bedrock](https://gitlab.lbl.gov/hdl-libraries/bedrock) is largely an accumulation of Verilog codebase written over the past several years at LBNL. It contains @@ -33,9 +33,9 @@ A few comments regarding the codebase 1. All software is set up to easily run on *nix systems. 2. Currently everything is [built using GNU Make](build-tools/makefile.md). We are on an active lookout for other methods, if they're actually better for us. -3. iverilog is used for simulation. We are slowly starting to use [Verilator](https://www.veripool.org/wiki/verilator) as well +3. iverilog is used for simulation. We are slowly starting to use [Verilator](https://www.veripool.org/verilator/) as well (see badger) -4. Xilinx tools are used for synthesis, and starting to support [YoSys](http://www.clifford.at/yosys/) (again see badger) +4. Xilinx tools are used for synthesis, and starting to support [YoSys](https://yosyshq.net/yosys/) (again see badger) 5. This repository is connected to Gitlab CI. All simulation based tests run automatically upon every commit on the continuous integration server. This helps us move faster (without breaking things). A helpful subset of those tests diff --git a/badger/README.md b/badger/README.md index dd0fe032d..f16bf2a5e 100644 --- a/badger/README.md +++ b/badger/README.md @@ -139,7 +139,7 @@ Demonstrating both Packet Badger functionality, and the test framework's ability to attach the simulation to the host's Ethernet subsystem. Your development machine needs to provide a traditional unix-y environment, -e.g., make, cc, python, awk, cmp. Also some version of [Icarus Verilog](http://iverilog.icarus.com/); see [status.md](status.md) for more details. +e.g., make, cc, python, awk, cmp. Also some version of [Icarus Verilog](https://steveicarus.github.io/iverilog/); see [status.md](status.md) for more details. In one shell session (Linux terminal), try: diff --git a/badger/crc8e_guts.v b/badger/crc8e_guts.v index af0c7c473..d9562aa52 100644 --- a/badger/crc8e_guts.v +++ b/badger/crc8e_guts.v @@ -7,7 +7,7 @@ module crc8e_guts( output [7:0] d_out, output zero ); -// http://en.wikipedia.org/wiki/Cyclic_redundancy_check +// https://en.wikipedia.org/wiki/Cyclic_redundancy_check parameter wid=32; parameter init=32'hffffffff; diff --git a/badger/doc/Makefile b/badger/doc/Makefile index 46599cc92..88a2ae5bc 100644 --- a/badger/doc/Makefile +++ b/badger/doc/Makefile @@ -1,6 +1,7 @@ # Commentary in ../tests/Makefile XCIRCUIT = xcircuit XVFB = xvfb-run -a -s "-screen 0 1440x900x24" +PANDOC = pandoc all: svg .PHONY: svg html @@ -14,10 +15,10 @@ html: ../index.html ../status.html echo "page load $<; svg; exit" > .xcircuitrc; $(XVFB) $(XCIRCUIT); rm .xcircuitrc ../index.html: ../README.md - pandoc -t html $< | sed -e 's,status\.md,status.html,g' > $@ + $(PANDOC) -t html $< | sed -e 's,status\.md,status.html,g' > $@ ../status.html: ../status.md - pandoc -t html -o $@ $< + $(PANDOC) -t html -o $@ $< clean: rm -f *.svg *.html diff --git a/badger/tests/Makefile b/badger/tests/Makefile index 9c52d38b6..dabc92699 100644 --- a/badger/tests/Makefile +++ b/badger/tests/Makefile @@ -38,7 +38,6 @@ VLATOR_LINT_IGNORE += -Wno-UNUSED -Wno-DECLFILENAME # Configuration not covered (yet?) by Bedrock's top_rules.mk VCD_ARGS = $(VCD_ARGS_$@) VVP_FLAGS = ${VVP_FLAGS_$@} ${VCD_ARGS_$@} -PERL = perl XCIRCUIT = xcircuit # XXX consider converting this to something more compatible with Bedrock's VIVADO_SYNTH. VIVADOEXEC = vivado @@ -89,10 +88,10 @@ crc_selfcheck: crc_selfcheck.o crc32.o derive_tb: crc_genguts.vh crc_genguts.vh: crc_derive - ./$^ 16 0x1021 32 > $@ + ./$< 16 0x1021 32 > $@ crc8e_guts.vh: crc_derive - ./$^ -lsb 32 0x04C11DB7 8 > $@ + ./$< -lsb 32 0x04C11DB7 8 > $@ # New feature compared to PSPEPS: no include file (or path) needed! # crc8e_guts.v is pre-filled in. The following steps give its derivation diff --git a/badger/tests/ac701/ac701_rgmii.xdc b/badger/tests/ac701/ac701_rgmii.xdc index 8eec1e2d1..37bba2b79 100644 --- a/badger/tests/ac701/ac701_rgmii.xdc +++ b/badger/tests/ac701/ac701_rgmii.xdc @@ -15,7 +15,7 @@ set_property -dict {PACKAGE_PIN U15 IOSTANDARD HSTL_I_18} [get_ports {RGMII_TXD[ set_property -dict {PACKAGE_PIN T18 IOSTANDARD HSTL_I_18} [get_ports {RGMII_TXD[2]}] set_property -dict {PACKAGE_PIN T17 IOSTANDARD HSTL_I_18} [get_ports {RGMII_TXD[3]}] -# https://www.xilinx.com/support/answers/53092.html +# https://adaptivesupport.amd.com/s/article/53092?language=en_US set_property IOB TRUE [get_ports {RGMII_TXD[*]}] set_property IOB TRUE [get_ports RGMII_TX_CTRL] set_property IOB TRUE [get_ports {RGMII_RXD[*]}] diff --git a/badger/tests/badger.tcl b/badger/tests/badger.tcl index f806200ed..7d70f9736 100644 --- a/badger/tests/badger.tcl +++ b/badger/tests/badger.tcl @@ -8,7 +8,7 @@ proc project_rpt {dest_dir} { report_datasheet -v -file $dest_dir/imp_datasheet.rpt report_cdc -v -details -file $dest_dir/cdc_report.rpt report_timing_summary -delay_type min_max -report_unconstrained -check_timing_verbose -max_paths 10 -input_pins -file $dest_dir/imp_timing.rpt - # http://xillybus.com/tutorials/vivado-timing-constraints-error + # https://xillybus.com/tutorials/vivado-timing-constraints-error if {! [string match -nocase {*timing constraints are met*} [report_timing_summary -no_header -no_detailed_paths -return_string]]} { puts "Timing constraints weren't met. Please check your design." exit 2 diff --git a/badger/tests/cluster_run.gold b/badger/tests/cluster_run.gold index c26a3b98b..313c90803 100644 --- a/badger/tests/cluster_run.gold +++ b/badger/tests/cluster_run.gold @@ -1,9 +1,9 @@ a3633a315dc8a61e6c2c81b0433258e71dcb43ad a3633a315dc8a61e6c2c81b0433258e71dcb43ad a3633a315dc8a61e6c2c81b0433258e71dcb43ad -scratch_out 00001e61 -scratch_in_r 00001e61 -scratch_out 000022b8 -scratch_in_r 000022b8 -scratch_out 0000270f -scratch_in_r 0000270f +scratch_out 1e61 +scratch_in_r 1e61 +scratch_out 22b8 +scratch_in_r 22b8 +scratch_out 270f +scratch_in_r 270f diff --git a/badger/tests/crc8e_guts_x.v b/badger/tests/crc8e_guts_x.v index ddcc786e6..ca7d23fa8 100644 --- a/badger/tests/crc8e_guts_x.v +++ b/badger/tests/crc8e_guts_x.v @@ -7,7 +7,7 @@ module crc8e_guts( output [7:0] d_out, output zero ); -// http://en.wikipedia.org/wiki/Cyclic_redundancy_check +// https://en.wikipedia.org/wiki/Cyclic_redundancy_check parameter wid=32; parameter init=32'hffffffff; diff --git a/badger/tests/gtkw_print.tcl b/badger/tests/gtkw_print.tcl index 9a46962be..e6558968b 100644 --- a/badger/tests/gtkw_print.tcl +++ b/badger/tests/gtkw_print.tcl @@ -4,6 +4,6 @@ set n1 [ string length $fn ] set n2 [ string last ".vcd" $fn ] if { $n2+4==$n1 } { set fpdf [ string range $fn 0 [ expr $n2-1 ] ] } else { set fpdf $fn } append fpdf ".pdf" -# https://github.com/acklinr/gtkwave/blob/master/examples/des.tcl +# https://github.com/gtkwave/gtkwave/blob/master/examples/des.tcl gtkwave::/File/Print_To_File PDF {Letter (8.5" x 11")} Full $fpdf gtkwave::/File/Quit diff --git a/badger/tests/kc705/ODDR.v b/badger/tests/kc705/ODDR.v index 95c29833b..8062451ce 100644 --- a/badger/tests/kc705/ODDR.v +++ b/badger/tests/kc705/ODDR.v @@ -1,5 +1,5 @@ // pathetic model of Xilinx DDR output cell -// ignores set and reset inputs +// ignores set and reset inputs, and the SRTYPE and DDR_CLK_EDGE parameters module ODDR ( input S, input R, @@ -14,10 +14,11 @@ parameter DDR_CLK_EDGE = "SAME_EDGE"; parameter INIT = 0; parameter SRTYPE = "SYNC"; -reg qx=INIT, hold=INIT; -always @(posedge C) if (CE) qx <= D1; -always @(posedge C) if (CE) hold <= D2; -always @(negedge C) qx <= hold; -assign Q = qx; +reg hold1=INIT, hold2=INIT; +always @(posedge C) if (CE) begin + hold1 <= D1; + hold2 <= D2; +end +assign Q = C ? hold1 : hold2; endmodule diff --git a/badger/tests/kc705/kc705_gmii.xdc b/badger/tests/kc705/kc705_gmii.xdc index a6838c5a9..114218b81 100644 --- a/badger/tests/kc705/kc705_gmii.xdc +++ b/badger/tests/kc705/kc705_gmii.xdc @@ -26,7 +26,7 @@ set_property -dict {PACKAGE_PIN K26 IOSTANDARD LVCMOS25} [get_ports {GMII_TXD[5] set_property -dict {PACKAGE_PIN L30 IOSTANDARD LVCMOS25} [get_ports {GMII_TXD[6]}] set_property -dict {PACKAGE_PIN J28 IOSTANDARD LVCMOS25} [get_ports {GMII_TXD[7]}] -# https://www.xilinx.com/support/answers/53092.html +# https://adaptivesupport.amd.com/s/article/53092?language=en_US set_property IOB TRUE [get_ports GMII_TXD[*]] set_property IOB TRUE [get_ports GMII_TX_EN] set_property IOB TRUE [get_ports GMII_RXD[*]] diff --git a/badger/tests/teststand_ac701.sh b/badger/tests/teststand_ac701.sh index 48272720b..3b7841ad3 100644 --- a/badger/tests/teststand_ac701.sh +++ b/badger/tests/teststand_ac701.sh @@ -4,7 +4,7 @@ # Tested useful for the CI test stand (mohs) at LBNL. # Can also act as a template for other use cases. set -e -xc3sprog -c jtaghs1_fast ac701_rgmii_vtest.bit +xc3sprog -c jtaghs1_fast $SERIAL_NUM_OPT ac701_rgmii_vtest.bit echo "So far so good" sleep 8 echo "Hope links are up" diff --git a/badger/tests/udp_model.c b/badger/tests/udp_model.c index 41b1fa89f..a3cb26a3d 100644 --- a/badger/tests/udp_model.c +++ b/badger/tests/udp_model.c @@ -110,7 +110,7 @@ void udp_receiver(int *in_octet, int *in_valid, int *in_count, int thinking) struct udp_state *udp_setup_r(unsigned short udp_port_, int badger_client_) { - struct udp_state *ust = US_P calloc(sizeof (struct udp_state), 1); + struct udp_state *ust = US_P calloc(1, sizeof (struct udp_state)); ust->sleepctr=0; ust->sleepmax=10; fprintf(stderr, "udp_receiver initializing UDP port %u. Interface mode: ", udp_port_); @@ -121,8 +121,8 @@ struct udp_state *udp_setup_r(unsigned short udp_port_, int badger_client_) } ust->badger_client = badger_client_; /* following could be combined by making second argument to calloc a 2? */ - struct pbuf *inbuf = ust->inbuf = PBUF_P calloc(sizeof(struct pbuf), 1); - struct pbuf *outbuf = ust->outbuf = PBUF_P calloc(sizeof(struct pbuf), 1); + struct pbuf *inbuf = ust->inbuf = PBUF_P calloc(1, sizeof(struct pbuf)); + struct pbuf *outbuf = ust->outbuf = PBUF_P calloc(1, sizeof(struct pbuf)); if (!inbuf || !outbuf) { perror("calloc"); exit(1); diff --git a/board_support/bmb7_kintex/jxj_gate.mk b/board_support/bmb7_kintex/jxj_gate.mk index 243a0bff4..ffff9f83f 100644 --- a/board_support/bmb7_kintex/jxj_gate.mk +++ b/board_support/bmb7_kintex/jxj_gate.mk @@ -23,7 +23,7 @@ COMMON_HDL_DIR = submodules/common-hdl $(VVP) $< $(VFLAGS) %.dat: %_tb - vvp $< > $@ + $(VVP) $< > $@ all: jxj_gate_tb diff --git a/board_support/fmc120/firmware/fmc120.c b/board_support/fmc120/firmware/fmc120.c index f712ab90a..6a161b6a0 100644 --- a/board_support/fmc120/firmware/fmc120.c +++ b/board_support/fmc120/firmware/fmc120.c @@ -15,7 +15,7 @@ /* JESD204B subclass1 deterministic latency - http://www.ti.com/lit/ml/slap159/slap159.pdf + https://www.ti.com/lit/ml/slap159/slap159.pdf ADC: ADS54J60 * LMFS: 4 2 1 1 * ceil(17/F) <= K <= min(32, floor(1024/F)) diff --git a/board_support/fmc150/dac3283.v b/board_support/fmc150/dac3283.v index 79e0edc49..b756b3afb 100644 --- a/board_support/fmc150/dac3283.v +++ b/board_support/fmc150/dac3283.v @@ -1,6 +1,6 @@ // DAC3283 Dual-Channel, 16-Bit, 800 MSPS, Digital-to-Analog Converter // LVDS PHY interface -// [1]: http://www.ti.com/lit/ds/symlink/dac3283.pdf +// [1]: https://www.ti.com/lit/ds/symlink/dac3283.pdf module dac3283 #( parameter [7:0] BASE_ADDR = 8'h0, diff --git a/board_support/fmc150/firmware/cdce72010.h b/board_support/fmc150/firmware/cdce72010.h index 036588589..7fb317933 100644 --- a/board_support/fmc150/firmware/cdce72010.h +++ b/board_support/fmc150/firmware/cdce72010.h @@ -1,7 +1,7 @@ // Ten Output High Performance Clock Synchronizer, // Jitter Cleaner, and Clock Distributor // Controlled over SPI, used on FMC150 -// See: http://www.ti.com/lit/ds/scas858c/scas858c.pdf +// See: https://www.ti.com/lit/ds/scas858c/scas858c.pdf #ifndef CDCE72010_H #define CDCE72010_H diff --git a/board_support/marble_soc/firmware/marble.c b/board_support/marble_soc/firmware/marble.c index c1e1cc949..1b67044cb 100644 --- a/board_support/marble_soc/firmware/marble.c +++ b/board_support/marble_soc/firmware/marble.c @@ -2,6 +2,7 @@ #include "i2c_soft.h" #include "sfr.h" #include "xadc.h" +#include "timer.h" #include "localbus.h" #include "settings.h" #include "print.h" @@ -34,9 +35,9 @@ marble_dev_t marble = { .module_present = false, .page_select = 0, .i2c_mux_sel = I2C_SEL_QSFP2, .i2c_addr = I2C_ADR_QSFP}, .adn4600 = { - .i2c_mux_sel = I2C_SEL_CLK, .i2c_addr = I2C_ADR_ADN4600}, + .i2c_mux_sel = I2C_SEL_CLK, .i2c_addr = I2C_ADR_ADN4600, .refdes = "U2"}, .si570 = { - .f_xtal_hz = 114285000ULL, .rfreq = 0ULL, + .f_reset_hz = 0, .rfreq = 0ULL, .i2c_mux_sel = I2C_SEL_APPL, .i2c_addr = I2C_ADR_SI570_NBB} }; @@ -107,20 +108,20 @@ void get_qsfp_info(qsfp_info_t *qsfp_param) { marble_i2c_read(qsfp_param->i2c_addr, 3, &qsfp_param->chan_stat_los, 1); marble_i2c_read(qsfp_param->i2c_addr, 22, buf, 2); - qsfp_param->temperature = (uint16_t)(buf[0] << 8 | buf[1]) >> 8; // C + qsfp_param->temperature = (int16_t)(buf[0] << 8 | buf[1]) >> 8; // C marble_i2c_read(qsfp_param->i2c_addr, 26, buf, 2); - qsfp_param->voltage = (int16_t)(buf[0] << 8 | buf[1]) / 10; // mV + qsfp_param->voltage = (uint16_t)(buf[0] << 8 | buf[1]) / 10; // mV marble_i2c_read(qsfp_param->i2c_addr, 42, buf, 8); for (i=0; i < 4; i++) { - qsfp_param->bias_current[i] = (int16_t)(buf[2*i] << 8 | buf[2*i+1]) * 2; // µA + qsfp_param->bias_current[i] = (uint16_t)(buf[2*i] << 8 | buf[2*i+1]) * 2; // microA } marble_i2c_read(qsfp_param->i2c_addr, 50, buf, 8); for (i=0; i < 4; i++) { - qsfp_param->tx_power[i] = (int16_t)(buf[2*i] << 8 | buf[2*i+1]) / 10; // µW + qsfp_param->tx_power[i] = (uint16_t)(buf[2*i] << 8 | buf[2*i+1]) / 10; // microW } marble_i2c_read(qsfp_param->i2c_addr, 34, buf, 8); for (i=0; i < 4; i++) { - qsfp_param->rx_power[i] = (int16_t)(buf[2*i] << 8 | buf[2*i+1]) / 10; // µW + qsfp_param->rx_power[i] = (uint16_t)(buf[2*i] << 8 | buf[2*i+1]) / 10; // microW } } @@ -158,6 +159,22 @@ bool get_adn4600_info(adn4600_info_t *info) { return ret; } +bool reset_si570(si570_info_t *info) { + bool ret = true; + uint8_t reg_135 = (1<<7); + ret &= marble_i2c_mux_set(info->i2c_mux_sel); + // reset si570 to read default registers, corresponding to f_reset_hz + i2c_write_regs(info->i2c_addr, 135, ®_135, 1); + DELAY_MS(10); + info->f_out_hz = info->f_reset_hz; + + // calculate f_xtal_hz + ret &= get_si570_info(info); + debug_printf(" %s: SI570 f_xtal: %12lu kHz\n", __func__, marble.si570.f_xtal_hz / 1000); + debug_printf(" %s: SI570 f_out : %12lu kHz\n", __func__, marble.si570.f_out_hz / 1000); + return ret; +} + bool get_si570_info(si570_info_t *info) { bool ret = true; unsigned char *regs = &info->regs[0]; @@ -168,14 +185,61 @@ bool get_si570_info(si570_info_t *info) { } info->hs_div = (regs[0] >> 5) + 4; info->n1 = (((regs[0] & 0x1f) << 2) | (regs[1] >> 6)) + 1; - info->rfreq = ((uint64_t)(regs[1] & 0x3f) << 32) + ((uint64_t)regs[2] << 24) + ((uint64_t)regs[3] << 16) + info->rfreq = ((uint64_t)(regs[1] & 0x3f) << 32) + + ((uint64_t)regs[2] << 24) + + ((uint64_t)regs[3] << 16) + ((uint64_t)regs[4] << 8) + (uint64_t)regs[5]; - info->f_out_hz = info->rfreq * info->f_xtal_hz / (info->hs_div * info->n1) / (1 << 28); + info->f_dco_hz = info->f_out_hz * info->hs_div * info->n1; + info->f_xtal_hz = info->f_dco_hz * (1 << 28) / info->rfreq; return ret; } -bool set_si570_info(si570_info_t *info, marble_init_byte_t *p_data) { +bool calc_si570_dividers(si570_info_t *info, uint64_t f1_hz) { + uint8_t hs_divs[] = {11, 9, 7, 6, 5, 4}; + uint64_t fdco; + uint8_t n1; + for (uint8_t i=0; i<65; i++) { + n1 = (i==0) ? 1 : i * 2; + for (uint8_t j=0; j<6; j++) { + fdco = f1_hz * n1 * hs_divs[j]; + if (fdco > 5670000000) break; + if (fdco > 4850000000) { + info->hs_div = hs_divs[j]; + info->n1 = n1; + info->f_dco_hz = fdco; + info->rfreq = fdco * (1 << 28) / info->f_xtal_hz; + debug_printf(" %s: SI570 HSDIV: %12u\n", __func__, marble.si570.hs_div); + debug_printf(" %s: SI570 N1 : %12u\n", __func__, marble.si570.n1); + debug_printf(" %s: SI570 f_dco: %12lu MHz\n", __func__, marble.si570.f_dco_hz / 1000000); + debug_printf(" %s: SI570 rfreq: %12u\n", __func__, marble.si570.rfreq); + return true; + } + } + } + printf(" %s: Failed to find valid si570 dividers.\n", __func__); + return false; +} + +bool calc_si570_regs(si570_info_t *info, uint64_t f1_hz) { bool ret = true; + unsigned char *regs = &info->regs[0]; + ret &= reset_si570(info); + ret &= calc_si570_dividers(info, f1_hz); + uint8_t n1 = info->n1 - 1; + uint8_t hs_div = info->hs_div - 4; + regs[0] = (hs_div << 5) | ((n1 & 0x7C) >> 2); // reg 7: hs_div[2:0], n1[6:2] + regs[1] = ((n1 & 3) << 6) | (info->rfreq >> 32); // reg 8: n1[1:0] rfreq[37:32] + regs[2] = (info->rfreq >> 24) & 0xff; // reg 9: rfreq[31:24] + regs[3] = (info->rfreq >> 16) & 0xff; // reg 10: rfreq[23:16] + regs[4] = (info->rfreq >> 8) & 0xff; // reg 11: rfreq[15:8] + regs[5] = info->rfreq & 0xff; // reg 12: rfreq[7:0] + return ret; +} + +bool set_si570_regs(si570_info_t *info, uint64_t f1_hz) { + bool ret = true; + unsigned char *regs = &info->regs[0]; + uint8_t reg_freeze_dco = (1<<4); uint8_t reg_unfreeze_dco = 0; uint8_t reg_newfreq = (1<<6); @@ -184,21 +248,24 @@ bool set_si570_info(si570_info_t *info, marble_init_byte_t *p_data) { // freeze DCO ret &= i2c_write_regs(info->i2c_addr, 137, ®_freeze_dco, 1); - // ret &= i2c_write_regmap_byte( - // info->i2c_addr, p_data->regmap, p_data->len); - t_reg8 *regmap = p_data->regmap; - for (unsigned ix=0; ixlen; ix++) { - i2c_write_regs(info->i2c_addr, regmap->addr + info->start_addr, &(regmap->data), 1); - regmap++; + for (unsigned ix=0; ix<6; ix++) { + ret &= marble_i2c_write(info->i2c_addr, info->start_addr+ix, regs+ix, 1); } // Unfreeze DCO ret &= i2c_write_regs(info->i2c_addr, 137, ®_unfreeze_dco, 1); + // Assert NewFreq bit ret &= i2c_write_regs(info->i2c_addr, 135, ®_newfreq, 1); + + // The process of freezing and unfreezing the DCO will + // cause the output clock to momentarily stop and start at + // any arbitrary point during a clock cycle. This process + // can take up to 10 ms. + DELAY_MS(20); + if (ret) info->f_out_hz = f1_hz; return ret; } - bool set_ina219_info(ina219_info_t *info, marble_init_word_t *p_data) { bool ret = true; ret &= marble_i2c_mux_set(info->i2c_mux_sel); @@ -250,18 +317,35 @@ void print_marble_status(void) { pca9555_info_t pca9555[2] = {marble.pca9555_qsfp, marble.pca9555_misc}; qsfp_info_t qsfp[2] = {marble.qsfp1, marble.qsfp2}; + switch (marble.variant) { + case MARBLE_VAR_MARBLE_V1_4: + printf(" %s: Marble Variant 1.4\n", __func__); + break; + case MARBLE_VAR_MARBLE_V1_3: + printf(" %s: Marble Variant 1.3\n", __func__); + break; + case MARBLE_VAR_MARBLE_V1_2: + printf(" %s: Marble Variant 1.2\n", __func__); + break; + case MARBLE_VAR_UNKNOWN: + default: + printf(" %s: Marble Variant Unknown\n", __func__); + break; + } + for (unsigned ix=0; ix<8; ix++) { printf(" %s: ADN4600: IN%1u -> OUT%1u\n", __func__, marble.adn4600.xpt_status[ix], ix); } // si570 register dump for (unsigned ix=0; ix<6; ix++) { - debug_printf(" %s SI570: addr = %1u, val = %#04x \n", + printf(" %s: SI570: addr = %1u, val = %#04x \n", __func__, marble.si570.start_addr+ix, marble.si570.regs[ix]); } - debug_printf(" %s: SI570 HSDIV: %12u\n", __func__, marble.si570.hs_div); - debug_printf(" %s: SI570 N1 : %12u\n", __func__, marble.si570.n1); - printf(" %s: SI570 f_out: %12lu kHz\n", __func__, marble.si570.f_out_hz / 1000); + printf(" %s: SI570 HSDIV : %12u\n", __func__, marble.si570.hs_div); + printf(" %s: SI570 N1 : %12u\n", __func__, marble.si570.n1); + printf(" %s: SI570 f_xtal: %12lu kHz\n", __func__, marble.si570.f_xtal_hz / 1000); + printf(" %s: SI570 f_out : %12lu kHz\n", __func__, marble.si570.f_out_hz / 1000); for (unsigned i=0; i<3; i++) { printf(" %s: INA219 %.4s, %.4s:\n", __func__, ina219[i].refdes, ina219[i].name); @@ -275,6 +359,8 @@ void print_marble_status(void) { printf(" %s: I0 : %#12X\n",__func__, pca9555[i].i0_val); printf(" %s: I1 : %#12X\n",__func__, pca9555[i].i1_val); } +// MICRO SIGN https://en.wikipedia.org/wiki/%CE%9C#Character_encodings +#define MICRO "\u00b5" for (unsigned i=0; i<2; i++) { if (qsfp[i].module_present) { printf(" %s: QSFP%1u Vendor : %.16s\n", __func__, i+1, qsfp[i].vendor_name); @@ -282,13 +368,13 @@ void print_marble_status(void) { printf(" %s: QSFP%1u Serial : %.16s\n", __func__, i+1, qsfp[i].serial_num); printf(" %s: QSFP%1u TXRX_LOS: %#8X\n", __func__, i+1, qsfp[i].chan_stat_los); printf(" %s: QSFP%1u Temp : %8d C\n", __func__, i+1, qsfp[i].temperature); - printf(" %s: QSFP%1u Volt : %8d mV\n", __func__, i+1, qsfp[i].voltage); + printf(" %s: QSFP%1u Volt : %8u mV\n", __func__, i+1, qsfp[i].voltage); for (unsigned j=0; j < 4; j++) { - printf(" %s: QSFP%1u TxBias %u: %8d µA\n", __func__, + printf(" %s: QSFP%1u TxBias %u: %8u " MICRO "A\n", __func__, i+1, j, qsfp[i].bias_current[j]); - printf(" %s: QSFP%1u TxPwr %u: %8d µW\n", __func__, + printf(" %s: QSFP%1u TxPwr %u: %8u " MICRO "W\n", __func__, i+1, j, qsfp[i].tx_power[j]); - printf(" %s: QSFP%1u RxPwr %d: %8u µW\n", __func__, + printf(" %s: QSFP%1u RxPwr %d: %8u " MICRO "W\n", __func__, i+1, j, qsfp[i].rx_power[j]); } } @@ -310,6 +396,8 @@ static void set_marble_variant(marble_init_t *init_data) { if ((mb4_pcb_rev >> 4) == 0x1) { marble.variant = mb4_pcb_rev & 0xf; printf(" %s: Found MMC Mailbox. mb4_pcb_rev = %x\n", __func__, mb4_pcb_rev); + } else { + printf(" %s: Invalid MMC Mailbox reading: mb4_pcb_rev = %x\n", __func__, mb4_pcb_rev); } #endif } else { // known Marble variant @@ -317,42 +405,54 @@ static void set_marble_variant(marble_init_t *init_data) { } } -static void configure_marble_variant(void) { - // look up si570 for i2c address: - // https://tools.skyworksinc.com/TimingUtility/timing-part-number-search-results.aspx - // https://www.skyworksinc.com/-/media/SkyWorks/SL/documents/public/data-sheets/Si570-71.pdf +// look up si570 for i2c address: +// https://tools.skyworksinc.com/TimingUtility/timing-part-number-search-results.aspx +// https://www.skyworksinc.com/-/media/SkyWorks/SL/documents/public/data-sheets/Si570-71.pdf +static void configure_si570_nbb_i2c(void) { + // 570NBB001808DGR, 20ppm + marble.si570.i2c_addr = I2C_ADR_SI570_NBB; + marble.si570.start_addr = 7; + marble.si570.f_reset_hz = 270000000ULL; +} +static void configure_si570_ncb_i2c(void) { + // 570NCB000933DG, 7ppm + marble.si570.i2c_addr = I2C_ADR_SI570_NCB; + marble.si570.start_addr = 13; + marble.si570.f_reset_hz = 125000000ULL; +} + +static void configure_marble_variant(void) { switch (marble.variant) { case MARBLE_VAR_MARBLE_V1_4: - printf("Marble Variant 1.4\n"); - marble.si570.i2c_addr = I2C_ADR_SI570_NBB; - marble.si570.start_addr = 7; + configure_si570_nbb_i2c(); break; case MARBLE_VAR_MARBLE_V1_3: case MARBLE_VAR_MARBLE_V1_2: - printf("Marble Variant 1.3 or 1.2\n"); - marble.si570.i2c_addr = I2C_ADR_SI570_NCB; - marble.si570.start_addr = 13; + configure_si570_ncb_i2c(); break; + // Auto-determine Marble variants, + // to support a project with mixed hardware versions case MARBLE_VAR_UNKNOWN: default: // in case mmc mailbox is not available, test i2c address for si570 - printf("Marble Variant Unknown\n"); - marble.si570.i2c_addr = I2C_ADR_SI570_NBB; - marble.si570.start_addr = 7; + // reset si570 to read f_xtal_hz + configure_si570_nbb_i2c(); if (get_si570_info(&marble.si570)) { printf(" %s: Found SI570 NBB (Marble 1.4)\n", __func__); + marble.variant = MARBLE_VAR_MARBLE_V1_4; break; } - marble.si570.i2c_addr = I2C_ADR_SI570_NCB; - marble.si570.start_addr = 13; + configure_si570_ncb_i2c(); if (get_si570_info(&marble.si570)) { printf(" %s: Found SI570 NCB (Marble 1.3).\n", __func__); + marble.variant = MARBLE_VAR_MARBLE_V1_3; break; } + printf(" %s: Failed to determine Marble variant).\n", __func__); break; } } @@ -367,16 +467,17 @@ bool init_marble(marble_init_t *init_data) set_marble_variant(init_data); configure_marble_variant(); - p = set_si570_info(&marble.si570, &init_data->si570_data); pass &= p; + p = calc_si570_regs(&marble.si570, init_data->si570_freq_hz); pass &= p; + p &= set_si570_regs(&marble.si570, init_data->si570_freq_hz); pass &= p; printf("==== SI570 init ==== : %s.\n", p?"PASS":"FAIL"); p = set_ina219_info(&marble.ina219_fmc1, &init_data->ina219_fmc1_data); pass &= p; - p = set_ina219_info(&marble.ina219_fmc2, &init_data->ina219_fmc2_data); pass &= p; - p = set_ina219_info(&marble.ina219_12v, &init_data->ina219_12v_data); pass &= p; + p &= set_ina219_info(&marble.ina219_fmc2, &init_data->ina219_fmc2_data); pass &= p; + p &= set_ina219_info(&marble.ina219_12v, &init_data->ina219_12v_data); pass &= p; printf("==== INA219 init ==== : %s.\n", p?"PASS":"FAIL"); p = set_pca9555_info(&marble.pca9555_qsfp, &init_data->pca9555_qsfp_data); pass &= p; - p = set_pca9555_info(&marble.pca9555_misc, &init_data->pca9555_misc_data); pass &= p; + p &= set_pca9555_info(&marble.pca9555_misc, &init_data->pca9555_misc_data); pass &= p; printf("==== PCA9555 init ==== : %s.\n", p?"PASS":"FAIL"); p = set_adn4600_info(&marble.adn4600, &init_data->adn4600_data); pass &= p; diff --git a/board_support/marble_soc/firmware/marble.h b/board_support/marble_soc/firmware/marble.h index fce2be748..26039c130 100644 --- a/board_support/marble_soc/firmware/marble.h +++ b/board_support/marble_soc/firmware/marble.h @@ -3,6 +3,11 @@ #include #include +#define MARBLE_VAR_MARBLE_V1_2 0 +#define MARBLE_VAR_MARBLE_V1_3 1 +#define MARBLE_VAR_MARBLE_V1_4 2 +#define MARBLE_VAR_UNKNOWN 3 + typedef union _DataDword { uint32_t value; unsigned char bytes[4]; @@ -33,23 +38,17 @@ typedef struct { t_reg8 *regmap; } marble_init_byte_t; -// # marble_v1_2 = 0, marble_v1_3 = 1, marble_v1_4 = 2 and so on.. -typedef enum { - MARBLE_VAR_MARBLE_V1_2, - MARBLE_VAR_MARBLE_V1_3, - MARBLE_VAR_MARBLE_V1_4, - MARBLE_VAR_UNKNOWN -} MARBLE_VAR; - typedef struct marble_init_t { - MARBLE_VAR marble_variant; + uint8_t marble_variant; marble_init_word_t ina219_fmc1_data; marble_init_word_t ina219_fmc2_data; marble_init_word_t ina219_12v_data; marble_init_byte_t pca9555_qsfp_data; // u34 marble_init_byte_t pca9555_misc_data; // u39 marble_init_byte_t adn4600_data; - marble_init_byte_t si570_data; + uint64_t si570_freq_hz; + bool enable_evr_gtx; + bool enable_poll_status; } marble_init_t; typedef struct ina219_info_t { @@ -82,9 +81,7 @@ typedef struct adn4600_info_t { const uint8_t i2c_mux_sel; const uint8_t i2c_addr; /** schematic refdes */ - const unsigned char refdes[4]; - /** function name */ - const unsigned char name[4]; + const unsigned char refdes[6]; uint8_t xpt_status[8]; } adn4600_info_t; @@ -106,11 +103,11 @@ typedef struct qsfp_info_t { int16_t temperature; /** Internally measured voltage, LSB 0.1 mV. Page 00h Byte 26-27 */ uint16_t voltage; - /** Tx bias current, LSB 2 µA, Page 00h Byte 42-49 */ + /** Tx bias current, LSB 2 microA, Page 00h Byte 42-49 */ uint16_t bias_current[4]; - /** Rx power, LSB 0.1 µW, Page 00h Byte 34-41 */ + /** Rx power, LSB 0.1 microW, Page 00h Byte 34-41 */ uint16_t rx_power[4]; - /** Tx power, LSB 0.1 µW, Page 00h Byte 50-57 */ + /** Tx power, LSB 0.1 microW, Page 00h Byte 50-57 */ uint16_t tx_power[4]; /** Page 00h Byte 148-163 */ unsigned char vendor_name[16]; @@ -129,16 +126,18 @@ typedef struct si570_info_t { const uint8_t i2c_mux_sel; /** I2C device address */ uint8_t i2c_addr; - /** start address */ - uint8_t start_addr; + unsigned char regs[6]; /** f_xtal, fixed, 0.09 ppb */ uint64_t f_xtal_hz; - unsigned char regs[6]; /** 38-bit fractional multiplier */ uint64_t rfreq; + uint64_t f_reset_hz; + uint64_t f_dco_hz; + uint64_t f_out_hz; + /** start address */ + uint8_t start_addr; uint8_t hs_div; uint8_t n1; - uint64_t f_out_hz; } si570_info_t; /** @@ -146,7 +145,7 @@ typedef struct si570_info_t { * @brief Structure holding marble board info */ typedef struct marble_dev_t { - MARBLE_VAR variant; + uint8_t variant; ina219_info_t ina219_12v; ina219_info_t ina219_fmc1; ina219_info_t ina219_fmc2; @@ -244,12 +243,26 @@ bool set_adn4600_info(adn4600_info_t *info, marble_init_byte_t *p_data); */ bool get_si570_info(si570_info_t *info); + /** - * Write SI570 registers + * Reset SI570 * @param info pointer to si570_info_t struct - * @param p_data pointer to marble_init_byte_t struct */ -bool set_si570_info(si570_info_t *info, marble_init_byte_t *p_data); +bool reset_si570(si570_info_t *info); + +/** + * Calculate SI570 registers from si570_info, and write + * @param info pointer to si570_info_t struct + * @param f1_hz new frequency in Hz + */ +bool calc_si570_regs(si570_info_t *info, uint64_t f1_hz); + +/** + * Write SI570 registers from si570_info + * @param info pointer to si570_info_t struct + * @param f1_hz new frequency in Hz + */ +bool set_si570_regs(si570_info_t *info, uint64_t f1_hz); /** * Poll marble board device info including ina219, pca9555, qsfp diff --git a/board_support/marblemini/marble.cfg b/board_support/marblemini/marble.cfg index 807b2b71f..f28768a4f 100644 --- a/board_support/marblemini/marble.cfg +++ b/board_support/marblemini/marble.cfg @@ -1,5 +1,5 @@ # openocd config file for Marble Mini -# https://github.com/BerkeleyLab/Marble-mini +# https://github.com/BerkeleyLab/Marble-Mini interface ftdi diff --git a/board_support/sp605/Makefile b/board_support/sp605/Makefile index 4d73ebb93..87960d181 100644 --- a/board_support/sp605/Makefile +++ b/board_support/sp605/Makefile @@ -1,26 +1,26 @@ ether_mc_sp605.bit: ether_mc_sp605.v $(ETH_MC_DEPS) $(FPGA_DIR)/sp60x_clocks.v config_romx_sp605.v sp605_gmii_base.ucf - PART=xc6slx45t-fgg484-3 $(SYNTH) ether_mc $^ + PART=xc6slx45t-fgg484-3 $(SYNTH) ether_mc $^ mv _xilinx/ether_mc.bit $@ ether_fllrf_sp605.bit: ether_fllrf_sp605.v $(BUILD_v_fllrf) $(FPGA_DIR)/sp60x_clocks.v config_romx_sp605.v sp605_gmii_base.ucf - PART=xc6slx45t-fgg484-3 $(SYNTH) ether_fllrf $^ + PART=xc6slx45t-fgg484-3 $(SYNTH) ether_fllrf $^ mv _xilinx/ether_fllrf.bit $@ ether_mgt_sp605_tb: ether_gtp_sp605_tb.v $(S6GTP_SUPPORT) BUFG.v IBUFDS.v $(S6GTP_XILINX_SIM_MODULE) ## mgt support on Spartan 6 ether_mgt_sp605.bit: ether_mgt_sp605.v $(S6GTP_SUPPORT) $(FPGA_DIR)/sp60x_clocks.v config_romx_sp605.v sp605_mgt_base.ucf $(ETH_MGT_DEPS) - PART=xc6slx45t-fgg484-3 $(SYNTH) ether_mgt $^ + PART=xc6slx45t-fgg484-3 $(SYNTH) ether_mgt $^ mv _xilinx/ether_mgt.bit $@ ether_both_sp605.bit: ether_both_sp605.v $(S6GTP_SUPPORT) $(ETH_MC_DEPS) $(FPGA_DIR)/sp60x_clocks.v config_romx_sp605.v $(COMMON_HDL_DIR)/data_xdomain.v $(COMMON_HDL_DIR)/flag_xdomain.v sp605_mgt_gmii_base.ucf $(ETH_MGT_DEPS) $(TXU) - PART=xc6slx45t-fgg484-3 $(SYNTH) ether_both_sp605 $^ + PART=xc6slx45t-fgg484-3 $(SYNTH) ether_both_sp605 $^ mv _xilinx/ether_both_sp605.bit $@ ether_fmc_mgt_sp605.bit: ether_fmc_mgt_sp605.v ether_fmc_mgt.vh $(S6GTP_SUPPORT) $(ETH_MC_DEPS) $(FPGA_DIR)/sp60x_clocks.v config_romx_sp605.v sp605_mgt_fmc.ucf $(FMC_SUPPORT) $(DDR_SUPPORT) - PART=xc6slx45t-fgg484-3 $(SYNTH) ether_fmc_mgt $^ + PART=xc6slx45t-fgg484-3 $(SYNTH) ether_fmc_mgt $^ mv _xilinx/ether_fmc_mgt.bit $@ ether_fmc_mc_sp605.bit: ether_fmc_mc_sp605.v ether_fmc_mc.vh $(FPGA_DIR)/sp60x_clocks.v config_romx_sp605.v sp605_gmii_fmc.ucf $(FMC_SUPPORT) $(DDR_SUPPORT) - PART=xc6slx45t-fgg484-3 $(SYNTH) ether_fmc_mc $^ + PART=xc6slx45t-fgg484-3 $(SYNTH) ether_fmc_mc $^ mv _xilinx/ether_fmc_mc.bit $@ diff --git a/board_support/sp605/base.ucf b/board_support/sp605/base.ucf index 68a5e1334..85bf4021d 100644 --- a/board_support/sp605/base.ucf +++ b/board_support/sp605/base.ucf @@ -1,10 +1,10 @@ # Xilinx SP605 (Spartan-6 XC6SLX45T-FGG484) -# as documented in ug526.pdf -# http://www.xilinx.com/support/documentation/sp605.htm +# as documented in UG526 +# https://docs.amd.com/v/u/en-US/ug526 # Table 1-9: Ethernet PHY Connections # Connections to Marvell 88E1111 shown in comments -# http://www.marvell.com/products/tranceivers/alaska_gigabit_ethernet_transceivers/Alaska_88E1111-002.pdf +# https://www.marvell.com/content/dam/marvell/en/public-collateral/transceivers/marvell-phys-transceivers-alaska-88e1111-datasheet.pdf ##------- Pin Constraints ------- ## 200 MHz Clock input diff --git a/board_support/sp605/mgt.ucf b/board_support/sp605/mgt.ucf index 7d9b9c953..c13d3d8eb 100644 --- a/board_support/sp605/mgt.ucf +++ b/board_support/sp605/mgt.ucf @@ -1,6 +1,6 @@ # Xilinx SP605 (Spartan-6 XC6SLX45T-FGG484) -# as documented in ug526.pdf -# http://www.xilinx.com/support/documentation/sp605.htm +# as documented in UG526 +# https://docs.amd.com/v/u/en-US/ug526 ##------- Pin Constraints ------- ## GTP Mapping diff --git a/board_support/sp605/sp605_mgt_gmii_base.ucf b/board_support/sp605/sp605_mgt_gmii_base.ucf index d868ed565..aabe0bc08 100644 --- a/board_support/sp605/sp605_mgt_gmii_base.ucf +++ b/board_support/sp605/sp605_mgt_gmii_base.ucf @@ -1,10 +1,10 @@ # Xilinx SP605 (Spartan-6 XC6SLX45T-FGG484) -# as documented in ug526.pdf -# http://www.xilinx.com/support/documentation/sp605.htm +# as documented in UG526 +# https://docs.amd.com/v/u/en-US/ug526 # Table 1-9: Ethernet PHY Connections # Connections to Marvell 88E1111 shown in comments -# http://www.marvell.com/products/tranceivers/alaska_gigabit_ethernet_transceivers/Alaska_88E1111-002.pdf +# https://www.marvell.com/content/dam/marvell/en/public-collateral/transceivers/marvell-phys-transceivers-alaska-88e1111-datasheet.pdf ##------- Pin Constraints ------- diff --git a/board_support/zest/README.md b/board_support/zest/README.md index 3b8cbefee..79bfd962c 100644 --- a/board_support/zest/README.md +++ b/board_support/zest/README.md @@ -1,6 +1,6 @@ Zest is a dual LPC FMC mezzanine board with 8 ADCs and 2 DACs. -Full information about the Zest hardware is at: https://github.com/BerkeleyLab/zest +Full information about the Zest hardware is at: https://github.com/BerkeleyLab/Zest ### Using zest_setup.py diff --git a/board_support/zest/lmk01801.py b/board_support/zest/lmk01801.py index 429f563f7..3f9d6abcf 100644 --- a/board_support/zest/lmk01801.py +++ b/board_support/zest/lmk01801.py @@ -19,10 +19,10 @@ def R0( CLKout0_3_PD="0", POWERDOWN="0", RESET="0"): - return eval('0b' + "01001000" + CLKin1_MUX + CLKin1_DIV + CLKin0_MUX - + CLKin0_DIV + "11" + CLKin1_BUF_TYPE + CLKin0_BUF_TYPE - + CLKout12_13_PD + CLKout8_11_PD + CLKout4_7_PD - + CLKout0_3_PD + POWERDOWN + RESET) + return eval('0b' + "01001000" + CLKin1_MUX + CLKin1_DIV + CLKin0_MUX + + CLKin0_DIV + "11" + CLKin1_BUF_TYPE + CLKin0_BUF_TYPE + + CLKout12_13_PD + CLKout8_11_PD + CLKout4_7_PD + + CLKout0_3_PD + POWERDOWN + RESET) # Register R1 and R2, see section 8.5, especially Tables 8-13 and 8-14 def R1( @@ -68,8 +68,8 @@ def R3(self, CLKout12_13_ADLY="000000"): return eval('0b' + "00010" + SYNC1_AUTO + SYNC0_AUTO + SYNC1_FAST + SYNC0_FAST + "011" + NO_SYNC_CLKout12_13 + - NO_SYNC_CLKout8_11 + NO_SYNC_CLKout4_7 + NO_SYNC_CLKout0_3 - + SYNC1_POL_INV + SYNC0_POL_INV + "0" + SYNC1_QUAL + + NO_SYNC_CLKout8_11 + NO_SYNC_CLKout4_7 + NO_SYNC_CLKout0_3 + + SYNC1_POL_INV + SYNC0_POL_INV + "0" + SYNC1_QUAL + CLKout12_13_HS + CLKout12_13_ADLY) # Register R4, see section 8.7, especially table 8-22 @@ -85,8 +85,8 @@ def R5( CLKout8_11_DIV="001", # 1 CLKout4_7_DIV="001", # 1 CLKout0_3_DIV="001"): # 2 - return eval('0b' + "0000" + CLKout12_13_DIV + "00" + CLKout13_ADLY_SEL - + CLKout12_ADLY_SEL + CLKout8_11_DIV + CLKout4_7_DIV + + return eval('0b' + "0000" + CLKout12_13_DIV + "00" + CLKout13_ADLY_SEL + + CLKout12_ADLY_SEL + CLKout8_11_DIV + CLKout4_7_DIV + CLKout0_3_DIV) # Register 15, see section 8.9, especially table 8-27 diff --git a/board_support/zest/zest_if.sv b/board_support/zest/zest_if.sv index 9011453ad..a333502f4 100644 --- a/board_support/zest/zest_if.sv +++ b/board_support/zest/zest_if.sv @@ -35,7 +35,8 @@ interface zest_if ( assign {U2_D1NA, U2_D1NB, U2_D1NC, U2_D1ND} = {U2[14], U2[4], U2[26], U2[11]}; assign {U2_D1PA, U2_D1PB, U2_D1PC, U2_D1PD} = {U2[17], U2[8], U2[5], U2[12]}; assign {U2_DCON, U2_DCOP, U2_FCON, U2_FCOP} = {U2[9], U2[15], U2[10], U2[6]}; - assign {U3[10], U2[22], U4[26]} = {U2_PDWN, U2_CSB, U2_SCLK}; + assign {U2[22], U4[26]} = {U2_CSB, U2_SCLK}; + // don't set U3[10] to U2_PDWN, since it's set to U3_PDWN below wire U3_D0NA, U3_D0NB, U3_D0NC, U3_D0ND, U3_D0PA, U3_D0PB, U3_D0PC, U3_D0PD, U3_D1NA, U3_D1NB, U3_D1NC, U3_D1ND, U3_D1PA, U3_D1PB, U3_D1PC, U3_D1PD, @@ -77,9 +78,9 @@ interface zest_if ( wire U18_DOUT_RDY = U18[1]; assign {U18[0], U18[2], U18[3], U18[4]} = {U18_CLK, U18_CS, U18_DIN, U18_SCLK}; - // NOTE: Semantics of PMOD and HDMI connectors are application-dependent and + // NOTE: Semantics of Pmod and HDMI connectors are application-dependent and // thus not handled here - // PMOD - J18, J17 + // Pmod - J18, J17 // J19: HDMI // U33U1: TPS62110 DC-DC converter diff --git a/board_support/zest/zest_setup.py b/board_support/zest/zest_setup.py index beb77db84..34e2f178a 100644 --- a/board_support/zest/zest_setup.py +++ b/board_support/zest/zest_setup.py @@ -727,7 +727,7 @@ def log_step(_s, _smp, _seek, _set, _hld): HLD += 1 while HLD < 16: SEEK = self.seeksethldsmp(SET, HLD, SMP) - if (SEEK != SEEK_000): + if SEEK != SEEK_000: HLD_TIME = HLD break HLD += 1 @@ -743,7 +743,7 @@ def log_step(_s, _smp, _seek, _set, _hld): SET += 1 while SET < 16: SEEK = self.seeksethldsmp(SET, HLD, SMP) - if (SEEK != SEEK_000): + if SEEK != SEEK_000: SET_TIME = SET break SET += 1 @@ -766,7 +766,7 @@ def log_step(_s, _smp, _seek, _set, _hld): HLD += 1 while HLD < 16: SEEK = self.seeksethldsmp(SET, HLD, SMP) - if (SEEK != SEEK_SMP): + if SEEK != SEEK_SMP: HLD_OK = HLD break HLD += 1 @@ -778,7 +778,7 @@ def log_step(_s, _smp, _seek, _set, _hld): SET += 1 while SET < 16: SEEK = self.seeksethldsmp(SET, HLD, SMP) - if (SEEK != SEEK_SMP): + if SEEK != SEEK_SMP: SET_OK = SET break SET += 1 diff --git a/board_support/zest_soc/firmware/zest.c b/board_support/zest_soc/firmware/zest.c index a8056cf79..bf54d5b5e 100644 --- a/board_support/zest_soc/firmware/zest.c +++ b/board_support/zest_soc/firmware/zest.c @@ -37,6 +37,8 @@ const uint8_t g_zest_adcs[] = { ZEST_DEV_AD9653B }; +zest_status_t zest = {0}; + bool get_spi_ready(void) { return !CHECK_BIT(SPI_GET_STATUS(g_base_spi), BIT_CIPO); } @@ -232,7 +234,7 @@ bool check_ad9781_bist(void) { SET_SFR1(g_base_sfr, SFR_OUT_REG1, SFR_OUT_BIT_DAC1_ENABLE, 0); pass &= run_ad9781_bist(bitres_exp, 0); - // test case 2: PRBS on dac0, zero on dac1 + // test case 2: PRBS on dac1, zero on dac0 SET_SFR1(g_base_sfr, SFR_OUT_REG1, SFR_OUT_BIT_DAC0_ENABLE, 0); SET_SFR1(g_base_sfr, SFR_OUT_REG1, SFR_OUT_BIT_DAC1_ENABLE, 1); pass &= run_ad9781_bist(0, bitres_exp); @@ -243,6 +245,8 @@ bool check_ad9781_bist(void) { // two's complement binary mode write_zest_reg(ZEST_DEV_AD9781, 0x2, 0x0); + SET_SFR1(g_base_sfr, SFR_OUT_REG1, SFR_OUT_BIT_DAC0_ENABLE, 1); + SET_SFR1(g_base_sfr, SFR_OUT_REG1, SFR_OUT_BIT_DAC1_ENABLE, 1); return pass; } @@ -364,7 +368,7 @@ uint32_t read_zest_reg(zest_dev_t dev, uint32_t addr) { switch (dev) { case ZEST_DEV_LMK01801: // Not supported - printf("read_zest_reg: LMK01801 reading not supported.\n"); + debug_printf("read_zest_reg: LMK01801 reading not supported.\n"); return 0; case ZEST_DEV_AD9653A: case ZEST_DEV_AD9653B: @@ -382,7 +386,7 @@ uint32_t read_zest_reg(zest_dev_t dev, uint32_t addr) { << g_devinfo.data_len; break; default: - printf("read_zest_reg: Invalid Device.\n"); + debug_printf("read_zest_reg: Invalid Device.\n"); return 0; } SPI_SET_DAT_BLOCK( g_base_spi, inst ); @@ -537,44 +541,55 @@ void select_zest_addr(uint32_t base) { g_base_awg = base + ZEST_BASE2_AWG; } -void read_amc7823_adcs(void) { +void get_zest_status(zest_status_t *zest) { + for (size_t ix=0; ix<4; ix++) { + zest->zest_frequencies[ix] = read_zest_fcnt(ix); + } + for (size_t ix=0; ix<3; ix++) { + zest->zest_phases[ix] = read_clk_div_ph(ix); + } + for (size_t ix=0; ix<9; ix++) { + zest->amc7823_adcs[ix] = read_zest_reg(ZEST_DEV_AMC7823, ix); + } + for (size_t ix=0; ix<6; ix++) { + zest->ad7794_adcs[ix] = read_ad7794_channel(ix); + } +} + +void print_zest_status(void) { + printf("ZEST Frequencies:\n"); + for (size_t ix=0; ix<4; ix++) { + printf(" Fclk %8s: ", zest_fcnt_names[ix]); + print_udec_fix(zest.zest_frequencies[ix]*125, FCNT_WIDTH, 3); + printf(" MHz\n"); + } + // AM7823 ADC, 12 bits: // ADC5: LO = +2 dBm regx*2.5 // ADC6: Curr = 0.7 A regx*2.5 // ADC7: Volt = 3.3/2 V regx*2.5 // ADC8: Temp = 25 C regx*2.6*0.61 - 273 - uint16_t adc_vals[9]; - unsigned int ix; - for (ix=0; ix<9; ix++) { - adc_vals[ix] = read_zest_reg(ZEST_DEV_AMC7823, ix); - } - printf("ZEST AMC7823 ADC:\n"); - for (ix=0; ix<9; ix++) { - printf(" ADC %u Val: %#06x", ix, adc_vals[ix]); + for (size_t ix=0; ix<9; ix++) { + printf(" ADC %u Val: %#06x", ix, zest.amc7823_adcs[ix]); if (ix == 8) { - // printf(" Temp: %.3f [C]\n", (adc_vals[ix] & 0xfff) * 2.6 * 0.61 - 273); + // printf(" Temp: %.3f [C]\n", (adc_vals[ix] & 0xfff) * 2.6 * 0.61 - 273) / 0x3ff; printf(" Temp:"); - print_dec_fix((adc_vals[ix] & 0xfff)*50 - 8736, 5, 3); + print_dec_fix((zest.amc7823_adcs[ix] & 0xfff)*50 - 8736, 5, 3); printf("[C]\n"); } else { // printf(" Volt: %.3f [V]\n", (adc_vals[ix] & 0xfff) * 2.5 / 0xfff); printf(" Volt:"); - print_dec_fix((adc_vals[ix] & 0xfff) * 2.5, 12, 3); + print_dec_fix((zest.amc7823_adcs[ix] & 0xfff) * 2.5, 12, 3); printf("[V]\n"); } } -} -void read_ad7794_adcs(void) { - uint32_t adc_vals[6]; - unsigned int ix; printf("ZEST AD7794 ADC:\n"); - for (ix=0; ix<6; ix++) { - adc_vals[ix] = read_ad7794_channel(ix); - printf(" AIN %u: %#x", ix+1, adc_vals[ix]); + for (size_t ix=0; ix<6; ix++) { + printf(" AIN %u: %#x", ix+1, zest.ad7794_adcs[ix]); printf(" Volt:"); - print_dec_fix((adc_vals[ix]) * 1.17, 24, 3); // internal 1.17V ref + print_dec_fix((zest.ad7794_adcs[ix]) * 1.17, 24, 3); // internal 1.17V ref printf("[V]\n"); } } @@ -782,26 +797,11 @@ void test_adc_pn9(uint8_t len) { write_zest_reg(ZEST_DEV_AD9653_BOTH, 0x14, 0x07); // two's comp } -bool init_zest_dbg(uint32_t base, zest_init_t *init_data) { +bool init_zest_dbg(uint32_t base) { bool pass=true; // uint32_t fcnt; select_zest_addr(base); - // zest_init_data_t *p_ad9653_data = &(init_data->ad9653_data); - // printf("Reset BUFR 0: "); - // reset_zest_bufr(0); - // printf("Reset BUFR 1: "); - // reset_zest_bufr(1); - // printf("ZEST ADC init : "); - // write_zest_regs(ZEST_DEV_AD9653_BOTH, p_ad9653_data->regmap, p_ad9653_data->len); - - // test_adc_pn9(8); - // check_adc_prbs9(); - // align_ad9781(12); - // uint32_t *fcnt_exp = init_data->fcnt_exp; - // check_zest_freq(0, fcnt_exp[0]); - // fcnt = read_zest_fcnt(0); - // print_udec_fix(fcnt*125, FCNT_WIDTH, 3); check_ad9781_bist(); return pass; } diff --git a/board_support/zest_soc/firmware/zest.h b/board_support/zest_soc/firmware/zest.h index 14fbe777e..57194d9a0 100644 --- a/board_support/zest_soc/firmware/zest.h +++ b/board_support/zest_soc/firmware/zest.h @@ -82,6 +82,7 @@ typedef struct { uint32_t *fcnt_exp; // expected ADC0_DIV, ADC1_DIV, DAC_DCO, DSP_CLK int8_t *phs_center; // expected ADC0_DIV, ADC1_DIV, DAC_DCO uint8_t *ad9781_smp; // expected AD9781_SMP values + bool enable_poll_status; } zest_init_t; typedef struct { @@ -92,6 +93,15 @@ typedef struct { uint32_t data_mask; } zest_devinfo_t; +typedef struct zest_status_t +{ + uint32_t zest_frequencies[4]; // ADC0, ADC1, DAC_DCO, DSP_CLK + int16_t zest_phases[3]; // ADC0, ADC1, DAC_DCO + uint16_t amc7823_adcs[9]; + uint32_t ad7794_adcs[6]; +} zest_status_t; + + /***************************************************************************//** * @brief SYNC both A&B banks by writing R5 when SYNC0_AUTO high *******************************************************************************/ @@ -279,12 +289,21 @@ bool align_adc_clk_phase(uint8_t ch, int8_t center); *******************************************************************************/ bool check_ad9781_bist(void); +/***************************************************************************//** + * @brief Get zest status +*******************************************************************************/ +void get_zest_status(zest_status_t *zest); + +/***************************************************************************//** + * @brief Print zest status +*******************************************************************************/ +void print_zest_status(void); + /***************************************************************************//** * @brief Test function. * @param base - base address - * @param zest_init_data - pointer to init register data. * @return pass - true if all validation passes *******************************************************************************/ -bool init_zest_dbg(uint32_t base, zest_init_t *init_data); +bool init_zest_dbg(uint32_t base); #endif diff --git a/board_support/zest_soc/vita_57.1_pinout.txt b/board_support/zest_soc/vita_57.1_pinout.txt index b2f2d6665..00219af80 100644 --- a/board_support/zest_soc/vita_57.1_pinout.txt +++ b/board_support/zest_soc/vita_57.1_pinout.txt @@ -1,6 +1,6 @@ -#from https://fmchub.github.io/appendix/VITA57_FMC_HPC_LPC_SIGNALS_AND_PINOUT.html -# compactible with KC705 UG810 Appendix B -# compactible with FMC116_112_user_manual.pdf Table 8. +# from https://fmchub.github.io/appendix/VITA57_FMC_HPC_LPC_SIGNALS_AND_PINOUT.html +# compatible with KC705 UG810 Appendix B +# compatible with FMC116_112_user_manual.pdf Table 8 CLK0_M2C_N H5 CLK0_M2C_P H4 diff --git a/board_support/zest_soc/zest.v b/board_support/zest_soc/zest.v index 25e92ae2e..9fde85f49 100644 --- a/board_support/zest_soc/zest.v +++ b/board_support/zest_soc/zest.v @@ -245,7 +245,7 @@ assign PWR_EN = ~pwr_en_b; // ADC0_DIV, ADC1_DIV, DAC_DCO wire signed [PH_DIFF_DW-1:0] phdiff [N_ADC:0]; -wire [N_ADC:0] phdiff_val; +wire [N_ADC:0] phdiff_err; // DSP_CLK, ADC0_DIV, ADC1_DIV, DAC_DCO wire [27:0] f_clks [N_ADC+1:0]; wire pll_locked; @@ -318,14 +318,13 @@ generate for (ix=0; ix $@ + $(PANDOC) -t html $< > $@ clean: rm -f cdc_snitch.html diff --git a/build-tools/build_rom.py b/build-tools/build_rom.py index 5805803ae..595a6b8da 100644 --- a/build-tools/build_rom.py +++ b/build-tools/build_rom.py @@ -96,7 +96,8 @@ def verilog_rom(a, suffix="", prefix=""): '''\t%i'h%3.3x: dxx <= 16'h%4.4x;''' % (max_addr_size+1, ix, a[ix]) for ix in range(len(a)) ]) - outputMessage = ("// 16k x 16 ROM machine generated by python verilog_rom()", + outputMessage = ( + "// 16k x 16 ROM machine generated by python verilog_rom()", "module {}config_romx{}(".format(prefix, suffix), "\tinput clk,", "\tinput [" + str(max_addr_size) + ":0] address,", @@ -109,8 +110,7 @@ def verilog_rom(a, suffix="", prefix=""): "\tdefault: dxx <= 0;", "endcase", "endmodule", - "" - ) + "") return '\n'.join(outputMessage) @@ -126,13 +126,13 @@ def c_rom(a, suffix="", prefix=""): d_list = ["0x%4.4x" % d for d in a] d_list2 = [", ".join(d_list[ix*8:ix*8+8]) for ix in range((len(a)+7)//8)] config_data = " " + ",\n ".join(d_list2) - outputMessage = ("// 16k x 16 ROM machine generated by python c_rom()", + outputMessage = ( + "// 16k x 16 ROM machine generated by python c_rom()", "static uint16_t config_romx[] = {", config_data, "};", "#define CONFIG_ROM_SIZE (%s)" % (2**max_addr_size), - "" - ) + "") return '\n'.join(outputMessage) diff --git a/build-tools/gen_features.py b/build-tools/gen_features.py index fb6b423cb..279a7e59e 100644 --- a/build-tools/gen_features.py +++ b/build-tools/gen_features.py @@ -82,7 +82,7 @@ def write_vlog(basename, cfg_dict, split=False, verbose=False): # Use include guard for defines only FD.write("`ifndef __%(n)s__\n`define __%(n)s__\n\n" % {'n': basename.upper()}) for d, v in d_dict.items(): - if (v == 0): + if v == 0: FD.write("//") FD.write(d_string % d.upper()) FD.write("\n`endif // __%s__\n" % basename.upper()) diff --git a/build-tools/litex_meta.sh b/build-tools/litex_meta.sh index bbe4ec452..f5ba9feac 100644 --- a/build-tools/litex_meta.sh +++ b/build-tools/litex_meta.sh @@ -27,5 +27,5 @@ echo "d77081080f0c5109adc92d2730145228cb19633de7f2ff50ca3a4ec0cb341532 litex_se # Now that we're quite sure we have the litex_setup we want, # go ahead and run it. -python3 litex_setup.py init install --config standard +python3 litex_setup.py --init --update --tag 2023.08 --install --config standard echo "DONE" diff --git a/build-tools/make-demo/makefile.md.in b/build-tools/make-demo/makefile.md.in index 11cf1bcca..49f55f22a 100644 --- a/build-tools/make-demo/makefile.md.in +++ b/build-tools/make-demo/makefile.md.in @@ -135,7 +135,7 @@ There have been [many attempts](https://en.wikipedia.org/wiki/List_of_build_auto continuing today, to build on, enhance, or replace make. Two in particular attempt to address the needs of an HDL environment. - * [Hdlmake](https://www.ohwr.org/project/hdl-make/wikis/home) + * [Hdlmake](https://ohwr.org/project/hdl-make/-/wikis/home) * [FuseSoC](https://pypi.org/project/fusesoc/) Our experiments with these tools have generally been frustrating. diff --git a/build-tools/makefile.md b/build-tools/makefile.md index 7cbfc437b..147a70a7f 100644 --- a/build-tools/makefile.md +++ b/build-tools/makefile.md @@ -205,7 +205,7 @@ There have been [many attempts](https://en.wikipedia.org/wiki/List_of_build_auto continuing today, to build on, enhance, or replace make. Two in particular attempt to address the needs of an HDL environment. - * [Hdlmake](https://www.ohwr.org/project/hdl-make/wikis/home) + * [Hdlmake](https://ohwr.org/project/hdl-make/-/wikis/home) * [FuseSoC](https://pypi.org/project/fusesoc/) Our experiments with these tools have generally been frustrating. diff --git a/build-tools/newad.py b/build-tools/newad.py index 03048749c..ef2e269f2 100755 --- a/build-tools/newad.py +++ b/build-tools/newad.py @@ -69,7 +69,7 @@ def add_to_global_map(name, base_addr, sign, aw, dw, description): def generate_addresses( - fd, names, base, low_res=False, gen_mirror=False, plot_map=False + fd, names, base, low_res=False, gen_mirror=False, plot_map=False, lb_width=24 ): """ Generate addresses with increasing bitwidth @@ -89,9 +89,9 @@ def generate_addresses( bitwidth = gch[k][0] register_array_size = 1 << gch[k][0] if ( - gen_mirror - and mirror_base == -1 - and register_array_size <= MIN_MIRROR_ARRAY_SIZE + gen_mirror and + mirror_base == -1 and + register_array_size <= MIN_MIRROR_ARRAY_SIZE ): mirror_base = base mirror_bit_len = mirror_size.bit_length() @@ -113,10 +113,13 @@ def generate_addresses( ) mirror_clk_prefix = "lb" # TODO: This is a hack if fd: + # Note the historical, buggy definition of lb_width as one less than + # the actual address bus bit-width. + mirror_pattern = (mirror_base & (2**(lb_width+1)-1)) >> mirror_bit_len s = ( "`define MIRROR_WIDTH %d\n" "`define ADDR_HIT_MIRROR (%s_addr[`LB_HI:`MIRROR_WIDTH]==%d)\n" - % (mirror_bit_len, mirror_clk_prefix, mirror_base >> mirror_bit_len) + % (mirror_bit_len, mirror_clk_prefix, mirror_pattern) ) fd.write(s) sign = gch[k][2] @@ -146,6 +149,9 @@ def generate_addresses( "`define ADDR_HIT_%s (%s_addr%s[`LB_HI:%d]==%d) " "// %s bitwidth: %d, base_addr: %d\n" ) + # Note the historical, buggy definition of lb_width as one less than + # the actual address bus bit-width. + addr_pattern = (next_addr & (2**(lb_width+1)-1)) >> bitwidth fd.write( s % ( @@ -153,7 +159,7 @@ def generate_addresses( gch[k][4], gch[k][5], bitwidth, - next_addr >> bitwidth, + addr_pattern, gch[k][1], bitwidth, next_addr, @@ -179,7 +185,7 @@ def generate_addresses( def address_allocation( - fd, hierarchy, names, address, low_res=False, gen_mirror=False, plot_map=False + fd, hierarchy, names, address, low_res=False, gen_mirror=False, plot_map=False, lb_width=24 ): """ NOTE: The whole hierarchy thing is currently being bypassed @@ -194,7 +200,7 @@ def address_allocation( (b) generate addresses for signals outside the hierarchy (out_mod) """ if hierarchy == len(g_hierarchy): - return generate_addresses(fd, names, address, low_res, gen_mirror, plot_map) + return generate_addresses(fd, names, address, low_res, gen_mirror, plot_map, lb_width) h = g_hierarchy[hierarchy] in_mod, out_mod = [], [] if type(h) is list: @@ -209,15 +215,16 @@ def address_allocation( low_res, gen_mirror, plot_map, + lb_width, ) out_mod = [n for n in names if prefix not in n] else: for n in names: (in_mod if h in n else out_mod).append(n) address = address_allocation( - fd, hierarchy + 1, in_mod, address, low_res, gen_mirror, plot_map + fd, hierarchy + 1, in_mod, address, low_res, gen_mirror, plot_map, lb_width ) - return generate_addresses(fd, out_mod, address, low_res, gen_mirror, plot_map) + return generate_addresses(fd, out_mod, address, low_res, gen_mirror, plot_map, lb_width) from parser import Parser @@ -260,9 +267,9 @@ def print_decode_header(fi, modname, fo, dir_list, lb_width, gen_mirror, use_yos # Below only applies for modules with genvar constructions if modname in vfile_parser.self_map: obuf.write( - "`define AUTOMATIC_map " - + " ".join(vfile_parser.self_map[modname] if modname in vfile_parser.self_map else []) - + "\n" + "`define AUTOMATIC_map " + + " ".join(vfile_parser.self_map[modname] if modname in vfile_parser.self_map else []) + + "\n" ) if fo: with open(fo, "w") as fd: @@ -276,7 +283,7 @@ def write_address_header( addr_bufs = StringIO() addr_bufs.write("`define LB_HI %d\n" % lb_width) address_allocation( - addr_bufs, 0, sorted(gch.keys()), base_addr, low_res, gen_mirror, plot_map + addr_bufs, 0, sorted(gch.keys()), base_addr, low_res, gen_mirror, plot_map, lb_width ) with open(output_file, "w") as fd: fd.write(addr_bufs.getvalue()) @@ -363,7 +370,7 @@ def main(argv): "--lb_width", type=int, default=10, - help="Set the address width of the local bus from which the generated registers are decoded", + help="One less than the address width of the local bus (from which the generated registers are decoded)", ) parser.add_argument( "-b", diff --git a/build-tools/parser.py b/build-tools/parser.py index e17bbcedc..414118b65 100644 --- a/build-tools/parser.py +++ b/build-tools/parser.py @@ -416,6 +416,7 @@ def parse_vfile_yosys(self, stack, fin, fd, dlist, clk_domain, cd_indexed): for port, (net_info, port_info) in parsed_mod['external_nets'].items(): signal_type = net_info['attributes']['signal_type'] if 'signal_type' in net_info['attributes'] else None + port_cd = net_info['attributes']['cd'] if 'cd' in net_info['attributes'] else clk_domain signed = 'signed' if 'signed' in net_info else None direction = port_info['direction'] if 'direction' in port_info else None p = Port(port, @@ -425,7 +426,7 @@ def parse_vfile_yosys(self, stack, fin, fd, dlist, clk_domain, cd_indexed): signed, this_mod, signal_type, - clk_domain, + port_cd, cd_indexed, port_info != {}, **attributes) @@ -441,7 +442,7 @@ def parse_vfile_yosys(self, stack, fin, fd, dlist, clk_domain, cd_indexed): None, this_mod, 'plus-we-VOID', - clk_domain, + port_cd, cd_indexed, port_info != {}, **attributes) diff --git a/build-tools/portfind.py b/build-tools/portfind.py index e8340558e..352bd995c 100644 --- a/build-tools/portfind.py +++ b/build-tools/portfind.py @@ -208,7 +208,7 @@ def make_html(fname, param_list, port_list): print(hdata["implement_html"] + "\n") if True: print("

") - print("A GTKWave-generated") + print("A GTKWave-generated") print("timing diagram showing {} is shown here:" .format(hdata["timing_html"])) print("

\"timing\n" @@ -319,7 +319,7 @@ def make_rst(fname, param_list, port_list, mod_comment_list, with_timing=None): print("") print("A `GTKWave`_-generated timing diagram is shown below:") print("") - print(".. _`GTKWave`: http://gtkwave.sourceforge.net/") + print(".. _`GTKWave`: https://gtkwave.sourceforge.net/") print("") print(".. _fig:{}_timing:".format(fbase)) print(".. figure:: {}_timing.png".format(fbase)) diff --git a/build-tools/reverse_json.py b/build-tools/reverse_json.py index e4c15e012..fdab23d27 100644 --- a/build-tools/reverse_json.py +++ b/build-tools/reverse_json.py @@ -14,6 +14,7 @@ addr_found = {} name_found = {} fail = 0 +verbose = False # can we have a command-line switch to control this? # Bugs: # brittle to variations in Verilog code formatting @@ -40,10 +41,9 @@ def ponder_int(s): # stderr.write('p, pv, o = %s, %d, %d\n' % (p, pv, o)) return pv - o else: - stderr.write('ERROR: parameter %s not found?\n' % p) + stderr.write('ERROR: parameter %s not found? (%s)\n' % (p, s)) else: - stderr.write("ERROR: Couldn't" + ' understand "%s", using 31\n' % - s) + stderr.write("ERROR: Couldn't" + ' understand "%s", using 31\n' % s) return r @@ -117,7 +117,8 @@ def memorize(g): alias = None m1a = re.search(r";\s*//\s*alias:\s*(\w+)", line) if m1a: - # stderr.write('INFO: alias "%s"\n' % m1a.group(1)) + if verbose: + stderr.write('INFO: alias "%s"\n' % m1a.group(1)) alias = m1a.group(1) tbank = m1.group(2) if bank_state == "=armed=": @@ -131,7 +132,8 @@ def memorize(g): if "default" in line and ": reg_bank_" in line: m1 = re.search(r"default:\s*reg_bank_(\w)\s*<=\s*32'h", line) if m1: - # stderr.write('INFO: default %s\n' % line) + if verbose: + stderr.write('INFO: default %s\n' % line) tbank = m1.group(1) if bank_state != tbank: stderr.write(ehead + ' bank %s assignment found in bank %s stanza\n' % (tbank, bank_state)) @@ -148,12 +150,26 @@ def memorize(g): m2 = re.search(r"\binput\s+(signed)?\s*\[([^:]+):0\]\s*(\w+)", line) if m2: memorize(m2.group) + if "output" in line: + m2 = re.search(r"\boutput\s+(signed)?\s*\[([^:]+):0\]\s*(\w+)", line) + if m2: + memorize(m2.group) if any(x in line for x in ["parameter", "localparam"]): - m3 = re.search(r"\b(?:parameter|localparam)\s+(\w+)\s*=\s*(\d+);", line) + # This logic can handle parameter as a statement, or in the header of a module. + # It does get tripped up by the _last_ parameter of a module, + # that doesn't have a trailing comma. + m3 = re.search(r"\b(?:parameter|localparam)\s+(\w+)\s*=\s*(\d+)[;,]", line) + if m3: + p, v = m3.group(1), int(m3.group(2)) + param_db[p] = v + if verbose: + stderr.write('INFO: found parameter "%s" with value %d\n' % (p, v)) + m3 = re.search(r"\b(?:parameter|localparam)\s+integer\s+(\w+)\s*=\s*(\d+)[;,]", line) if m3: p, v = m3.group(1), int(m3.group(2)) param_db[p] = v - # stderr.write('INFO: found parameter "%s" with value %d\n' % (p, v)) + if verbose: + stderr.write('INFO: found parameter "%s" with value %d\n' % (p, v)) if "endcase" in line: bank_state = None if "case" in line and "addr" in line: diff --git a/build-tools/riscv_meta.sh b/build-tools/riscv_meta.sh index eefa2dc6c..20904d010 100644 --- a/build-tools/riscv_meta.sh +++ b/build-tools/riscv_meta.sh @@ -17,9 +17,10 @@ # Same gcc version, in fact! Slightly different configuration. # # I tried to peek at -# https://github.com/riscv/riscv-gnu-toolchain +# https://github.com/riscv-collab/riscv-gnu-toolchain # but that's kind of obfuscated. # http://www.ifp.illinois.edu/~nakazato/tips/xgcc.html +# (link is dead, but document can be found at archive.org) # is more to-the-point, but out-of-date (2007). # The numbered flow below is straight from nakazato. # This script is meant to be as short, sweet, and concrete as possible. @@ -32,11 +33,11 @@ # # Versions: # binutils-2.32, released 2019-02-02 -# http://www.gnu.org/software/binutils/ +# https://www.gnu.org/software/binutils/ # gcc-8.3.0, released 2019-02-22 -# http://www.gnu.org/software/gcc/ +# https://www.gnu.org/software/gcc/ # newlib-3.1.0, released 2018-12-31 -# ftp://sourceware.org/pub/newlib/index.html +# https://sourceware.org/newlib/ # # 0ab6c55dd86a92ed561972ba15b9b70a8b9f75557f896446c82e8b36e473ee04 binutils-2.32.tar.xz # 64baadfe6cc0f4947a84cb12d7f0dfaf45bb58b7e92461639596c21e02d97d2c gcc-8.3.0.tar.xz diff --git a/build-tools/riscv_prep.sh b/build-tools/riscv_prep.sh index 8f1547679..f58c0273f 100644 --- a/build-tools/riscv_prep.sh +++ b/build-tools/riscv_prep.sh @@ -7,7 +7,7 @@ set -e # Get upstream stable sources mkdir ref cd ref -wget --no-verbose http://mirrors.kernel.org/gnu/binutils/binutils-2.32.tar.xz +wget --no-verbose https://mirrors.kernel.org/gnu/binutils/binutils-2.32.tar.xz wget --no-verbose https://bigsearcher.com/mirrors/gcc/releases/gcc-8.3.0/gcc-8.3.0.tar.xz wget --no-verbose ftp://sourceware.org/pub/newlib/newlib-3.1.0.tar.gz diff --git a/build-tools/tblint.py b/build-tools/tblint.py index 8fb61ed05..4e993e718 100644 --- a/build-tools/tblint.py +++ b/build-tools/tblint.py @@ -252,7 +252,7 @@ def testMatch(argv): def doLint(argv): USAGE = ("python3 {0} filename\n" + - " Use python3 {0} --selftest to run regex syntax tests").format(argv[0]) + " Use python3 {0} --selftest to run regex syntax tests").format(argv[0]) if len(argv) < 2: print(USAGE) print(SYNTAX_RULES) diff --git a/build-tools/top_rules.mk b/build-tools/top_rules.mk index 9373d9d57..9adfcecc9 100644 --- a/build-tools/top_rules.mk +++ b/build-tools/top_rules.mk @@ -18,8 +18,10 @@ VERILATOR = verilator -Wall -Wno-fatal GTKWAVE = gtkwave VPIEXT = vpi PYTHON = python3 +PERL = perl AWK = awk XCIRCUIT = xcircuit +SV2V = sv2v YOSYS = yosys YOSYS_QUIET = -q YOSYS_JSON_OPTION = -DBUGGY_FORLOOP @@ -218,10 +220,10 @@ EMPTY := SPACE := $(EMPTY) $(EMPTY) COMMA := , -# http://www.graphviz.org/content/dot-language +# https://graphviz.org/doc/info/lang.html # apt-get install graphviz %.ps: %.dot dot -Tps $< -o $@ %_support.vh: $(BS_HARDWARE_DIR)/%_support.in - perl $(BUILD_DIR)/regmap_proc.pl $< > $@ + $(PERL) $(BUILD_DIR)/regmap_proc.pl $< > $@ diff --git a/build-tools/vivado_tcl/project_proc.tcl b/build-tools/vivado_tcl/project_proc.tcl index caf8e4e14..b8dc8dbee 100644 --- a/build-tools/vivado_tcl/project_proc.tcl +++ b/build-tools/vivado_tcl/project_proc.tcl @@ -237,7 +237,7 @@ proc project_rpt {project_name} { report_datasheet -v -file ./_xilinx/$project_name/imp_datasheet.rpt report_cdc -v -details -file ./_xilinx/$project_name/cdc_report.rpt report_timing_summary -delay_type min_max -report_unconstrained -check_timing_verbose -max_paths 10 -input_pins -file ./_xilinx/$project_name/imp_timing.rpt - # http://xillybus.com/tutorials/vivado-timing-constraints-error + # https://xillybus.com/tutorials/vivado-timing-constraints-error if {! [string match -nocase {*timing constraints are met*} [report_timing_summary -no_header -no_detailed_paths -return_string]]} { puts "Timing constraints weren't met. Please check your design." exit 2 diff --git a/build-tools/vivado_tcl/swap_gitid.tcl b/build-tools/vivado_tcl/swap_gitid.tcl index dd7609b77..8595fb147 100644 --- a/build-tools/vivado_tcl/swap_gitid.tcl +++ b/build-tools/vivado_tcl/swap_gitid.tcl @@ -180,16 +180,16 @@ proc check_impl {} { proc check_bmem {pattern width} { set c [get_cells -hier -filter {PRIMITIVE_TYPE =~ BMEM.*.*} $pattern] - if {[llength $c] != 1} { - puts "ERROR: Unexpected number of $pattern!" - return 0 - } - set n [get_property READ_WIDTH_A $c] - if {$n != $width} { - puts "ERROR: Read Width of $pattern must be $width (not $n)!" - return 0 + set xc {} + foreach cc $c { + set n [get_property READ_WIDTH_A $cc] + if {$n == $width} { + lappend xc $cc + } else { + puts "Ignoring instance $cc where Read Width is $width (not $n)" + } } - return $c + return $xc } proc swap_gitid {old_commit new_commit rowwidth dry_run} { @@ -200,7 +200,16 @@ proc swap_gitid {old_commit new_commit rowwidth dry_run} { 8 { set c0 [check_bmem "*dxx_reg_0" 9] set c1 [check_bmem "*dxx_reg_1" 9] - if {$c0 == 0 || $c1 == 0} {return 0} + if {$c0 == {} && $c1 == {}} { + puts "no relevant 8Kx8 BRAM found" + return 0 + } + set lc0 [llength $c0] + set lc1 [llength $c1] + if {$lc0 != 1 || $lc1 != 1} { + puts "ERROR: swap_gitid can't handle case $rowwidth $lc0 $lc1 yet!" + return 0 + } set init0 [get_property INIT_00 $c0] set init1 [get_property INIT_00 $c1] set xx [gitid_proc $old_commit $new_commit $init0 $init1 $rowwidth] @@ -216,17 +225,23 @@ proc swap_gitid {old_commit new_commit rowwidth dry_run} { 16 { set c0 [check_bmem "*dxx_reg" 18] - if {$c0 == 0} {return 0} - set init0 [get_property INIT_00 $c0] - set init1 [get_property INIT_01 $c0] - set xx [gitid_proc $old_commit $new_commit $init0 $init1 $rowwidth] - if {[llength $xx] != 2} {return 0} - lassign $xx init0x init1x - if {$dry_run != 1} { - set_property INIT_00 $init0x $c0 - set_property INIT_01 $init1x $c0 - } else { - puts "Dry run only" + if {$c0 == {}} { + puts "no relevant 4Kx16 BRAM found" + return 0 + } + foreach cc $c0 { + puts "trying $cc" + set init0 [get_property INIT_00 $cc] + set init1 [get_property INIT_01 $cc] + set xx [gitid_proc $old_commit $new_commit $init0 $init1 $rowwidth] + if {[llength $xx] != 2} {return 0} + lassign $xx init0x init1x + if {$dry_run != 1} { + set_property INIT_00 $init0x $cc + set_property INIT_01 $init1x $cc + } else { + puts "Dry run only" + } } } @@ -282,18 +297,19 @@ proc is_git_dirty {gitid} { } } -# Generate 40 digits git commit id where the latest 16 digits -# are zeros in case of local modification +# Generate 40-hex-digit git commit id, where the last 16 digits +# are zeros in case of local modification. +# This result is normally headed for swap_gitid. proc generate_extended_git_id {git_id dirtiness} { if {[string length $git_id] < 40} { error "generate_extended_git_id error: [ ]received a git id shorter than 40 digits!" } + set rr $git_id if {$dirtiness} { - return [string range $git_id 0 23]0000000000000000 - } else { - return $git_id + set rr [string range $git_id 0 23]0000000000000000 } + return [string toupper $rr] } # Print git hash in huge and colored way @@ -310,7 +326,6 @@ proc get_git_context {} { # single-source-of-truth array set git_status [get_full_git_id] # everything else derived from that - set git_id [string range $git_status(id) 0 39] set git_id_short [string range $git_status(id) 0 7] set git_dirty [is_git_dirty $git_status(id)] set dirty_suffix "" diff --git a/build-tools/xil_syn b/build-tools/xil_syn index e0daf8916..53ecd85ed 100644 --- a/build-tools/xil_syn +++ b/build-tools/xil_syn @@ -258,7 +258,7 @@ if [ "$arch" = "s6" ]; then else case "$PART" in # LLRF46 needs -g StartUpClk:CClk per page 1 of - # http://www.dimtel.com/_media/support/llrf4/packet46.pdf + # https://www.dimtel.com/_media/support/llrf4/packet46.pdf # otherwise the DAC outputs get messed up. xc6slx*) BG_OPT="-g StartUpClk:CClk" ;; *) BG_OPT="-g StartUpClk:JtagClk" ;; diff --git a/cmoc/Makefile b/cmoc/Makefile index 09aed6662..2befd6445 100644 --- a/cmoc/Makefile +++ b/cmoc/Makefile @@ -1,6 +1,7 @@ include ../dir_list.mk -vpath %.v $(RTSIM_DIR) $(DSP_DIR) $(DSP_DIR)/hosted $(BADGER_DIR) +vpath %.v $(RTSIM_DIR) $(DSP_DIR) $(DSP_DIR)/hosted $(BEDROCK_DIR)/localbus $(BADGER_DIR) +vpath %.c ../badger/tests .PHONY: all all: targets @@ -10,14 +11,14 @@ include $(BADGER_DIR)/rules.mk NEWAD_ARGS += -y -TEST_BENCH = xy_pi_clip_tb fdbk_core_tb tgen_tb circle_buf_tb cryomodule_tb cryomodule_badger_tb rf_controller_tb +TEST_BENCH = xy_pi_clip_tb fdbk_core_tb circle_buf_tb cryomodule_tb cryomodule_badger_tb rf_controller_tb RTEFI_CLIENT_LIST = hello.v speed_test.v mem_gateway.v include rules.mk TGT_ := $(TEST_BENCH) -NO_CHECK = tgen_check cryomodule_badger_check rf_controller_check +NO_CHECK = cryomodule_badger_check rf_controller_check CHK_ = $(filter-out $(NO_CHECK), $(TEST_BENCH:%_tb=%_check)) NO_LINT = $(NO_CHECK) LNT_ = $(filter-out $(NO_LINT), $(TEST_BENCH:%_tb=%_lint)) @@ -37,7 +38,7 @@ fdbk_core.vcd: $(AUTOGEN_DIR)/regmap_fdbk_core_tb.json fdbk_core.vcd: fdbk_core_tb fdbk_core_test.py $(PYTHON) fdbk_core_test.py fdbk_core_check: fdbk_core.vcd - @ echo DONE + @echo DONE cryomodule_in.dat: cryomodule_test_setup.py $(AUTOGEN_DIR)/regmap_cryomodule.json $(PYTHON) cryomodule_test_setup.py | sed -e 's/ *#.*//' | grep . > $@ @@ -56,7 +57,12 @@ $(AUTOGEN_DIR)/config_romx.v: VFLAGS_DEP += -y $(BADGER_DIR) -cryomodule_badger_tb.v: $(RTEFI_V) $(AUTOGEN_DIR)/config_romx.v cordicg_b22.v cryomodule_auto +VFLAGS_cryomodule_badger_tb = -m ./tap-vpi + +cryomodule_badger_tb: $(RTEFI_V) $(AUTOGEN_DIR)/config_romx.v cordicg_b22.v cryomodule_auto tap-vpi.vpi + +CFLAGS_tap-vpi.o = $(VPI_CFLAGS) -D_POSIX_C_SOURCE=200809L +tap-vpi.vpi: ethernet_model.o tap_alloc.o crc32.o CLEAN += $(RTEFI_CLEAN) @@ -82,6 +88,7 @@ endif # endif CLEAN += $(TGT_) $(CHK_) *.bit *.in *.vcd +CLEAN += *.o tap-vpi.vpi CLEAN += fdbk_core*.dat lim_step_file_in.dat setmp_step_file_in.dat cryomodule_in.dat cryomodule_p.dat cryomodule.dat $(RTEFI_CLEAN) CLEAN_DIRS += _xilinx include $(BUILD_DIR)/bottom_rules.mk diff --git a/cmoc/cryomodule.v b/cmoc/cryomodule.v index f2869dc71..1b70e275a 100644 --- a/cmoc/cryomodule.v +++ b/cmoc/cryomodule.v @@ -66,6 +66,7 @@ module cryomodule( input lb_read, output [31:0] lb_out ); +`undef AUTOMATIC_self // Note that the following five parameters should all be in the range 0 to 255, // in order to be properly read out via config_data0, below. @@ -104,8 +105,7 @@ wire lb2_clk = clk1x; parameter n_cycles = n_mech_modes * 2; parameter interp_span = 4; // ceil(log2(n_cycles)) -`define SLOW_SR_LEN 4*8 -parameter sr_length = `SLOW_SR_LEN; +parameter sr_length = 4*8; `ifndef SIMPLE_DEMO // Transfer local bus to clk2x domain @@ -239,13 +239,13 @@ generate for (cavity_n=0; cavity_n < cavity_count; cavity_n=cavity_n+1) begin: c assign slow_data_ready[cavity_n] = circle_data_ready[cavity_n] & ~slow_invalid; // XXX mixes domains, simulate to make sure it's glitch-free // Make our own additions to slow shift register // equivalence circle_stat: circle_fault 1, circle_wrap 1, circle_addr 14 -`define SLOW_SR_DATA { circle_count, circle_stat } + wire [sr_length-1:0] slow_sr_data = { circle_count, circle_stat }; // TODO: These `we_*` wires below, are taken from the decode signals that are auto generated wire [7:0] slow_shell_out; reg [sr_length-1:0] slow_read=0; always @(posedge clk1x) if (slow_op) begin - slow_read <= slow_snap ? `SLOW_SR_DATA : {slow_read[sr_length-9:0],slow_shell_out}; + slow_read <= slow_snap ? slow_sr_data : {slow_read[sr_length-9:0],slow_shell_out}; end assign slow_out = slow_read[sr_length-1:sr_length-8]; diff --git a/cmoc/cryomodule_badger.v b/cmoc/cryomodule_badger.v index 8c2d5fd44..21b449f36 100644 --- a/cmoc/cryomodule_badger.v +++ b/cmoc/cryomodule_badger.v @@ -14,7 +14,8 @@ module cryomodule_badger ( // Ethernet configuration port input eth_cfg_clk, input [9:0] eth_cfg_set, - output [7:0] eth_status + output [7:0] eth_status, + output thinking ); parameter ip ={8'd192, 8'd168, 8'd7, 8'd4}; @@ -58,10 +59,11 @@ rtefi_blob #(.ip(ip), .mac(mac)) badger( .p3_control_rd(rtefi_lb_control_rd), .p3_control_rd_valid(rtefi_lb_control_rd_valid), .p3_data_out(rtefi_lb_data_out), - .p3_data_in(rtefi_lb_data_in) + .p3_data_in(rtefi_lb_data_in), // // Dumb stuff to get LEDs blinking // output rx_mon, // output tx_mon, + .in_use(thinking) ); diff --git a/cmoc/cryomodule_badger_tb.v b/cmoc/cryomodule_badger_tb.v index 8f95d2002..797c73885 100644 --- a/cmoc/cryomodule_badger_tb.v +++ b/cmoc/cryomodule_badger_tb.v @@ -3,7 +3,7 @@ module cryomodule_badger_tb; // based mostly on aggregate_tb.v -parameter [31:0] ip = 32'd3232237316; // 192.168.7.4 +parameter [31:0] ip = {8'd192, 8'd168, 8'd7, 8'd4}; // 192.168.7.4 parameter [47:0] mac = 48'h112233445566; // Buffer memory to hold a packet read from a file @@ -40,6 +40,7 @@ reg [7:0] eth_in=0, eth_in_=0; reg eth_in_s=0, eth_in_s_=0; wire [7:0] eth_out; wire eth_out_s; +wire thinking; // hook to make things run efficiently reg eth_out_s1=0, ok_to_print=1; integer ci; @@ -48,7 +49,7 @@ always @(posedge clk) begin ci = cc % (data_len+150); if (use_tap) begin // Access to Linux tap interface, see tap-vpi.c - if (cc > 4) $tap_io(eth_out, eth_out_s, eth_in_, eth_in_s_); + if (cc > 4) $tap_io(eth_out, eth_out_s, eth_in_, eth_in_s_, thinking); eth_in <= eth_in_; eth_in_s <= eth_in_s_; end else if ((ci>=100) & (ci<(100+data_len))) begin diff --git a/cmoc/cryomodule_test_setup.py b/cmoc/cryomodule_test_setup.py index 4b8d6dd53..fbaeef7cb 100644 --- a/cmoc/cryomodule_test_setup.py +++ b/cmoc/cryomodule_test_setup.py @@ -3,7 +3,7 @@ from math import pi, sqrt, log from numpy import exp as cexp from numpy import ceil -# http://stackoverflow.com/questions/14132789/python-relative-imports-for-the-billionth-time +# https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time # Leaves me with only one choice ... :( # Since I don't want to modify shell variables sys.path.append( @@ -174,7 +174,7 @@ def fix(x, b, msg, opt=None): # prefix and name are used to give a helpful comment def set_reg(offset, prefix, name, regmap): val = globals()[name] # globals() or locals()? - if (type(val) is list): + if type(val) is list: for i, v in enumerate(val): print(offset + regmap[name] + i, v, "#", prefix + name + "[" + str(i) + "]") @@ -321,7 +321,7 @@ def push_seed(addr, hf): hf.update(chr(jx).encode('utf-8')) -if (prng_seed is not None): +if prng_seed is not None: from hashlib import sha1 print("# PRNG subsystem seed is '%s'" % prng_seed) hf = sha1() @@ -413,6 +413,6 @@ def delay_set(ticks, addr, data): print("%d 1 # Flip the circle buffer" % (0x13800)) print("%d 1 # Flip the circle buffer" % (0x13801)) -if (error_cnt > 0): +if error_cnt > 0: print("# %d scaling errors found" % error_cnt) exit(1) diff --git a/cmoc/fdbk_core.v b/cmoc/fdbk_core.v index 604d74b78..e93e0610b 100644 --- a/cmoc/fdbk_core.v +++ b/cmoc/fdbk_core.v @@ -33,6 +33,7 @@ module fdbk_core( input [1:0] coarse_scale, // external `AUTOMATIC_self ); +`undef AUTOMATIC_self // Knobs to use/bypass CORDIC multiplexer, Magnitude/Phase processor (slow), // and Low-Latency Processor (bypassing conversion to polar coordinates) diff --git a/cmoc/fdbk_core_tb.v b/cmoc/fdbk_core_tb.v index 289ec1311..56d1ec254 100644 --- a/cmoc/fdbk_core_tb.v +++ b/cmoc/fdbk_core_tb.v @@ -224,11 +224,14 @@ always @(posedge clk) begin sync_d2 <= sync_d; // Write aligned input, set-point and error signals onto file (set-point scaling test) - if (out_file != 0 && sync_d2 && (test_type==0 || test_type==1)) $fwrite(out_file," %d %d %d %d %d %d\n", setmp_d2, setmp_d, in_mp_d2, in_mp_d, mp_err_d, mp_err); + if (out_file != 0 && sync_d2 && (test_type==0 || test_type==1)) + $fwrite(out_file," %d %d %d %d %d %d\n", setmp_d2, setmp_d, in_mp_d2, in_mp_d, mp_err_d, mp_err); // Write aligned input and output signals onto file (feedback gain scaling test) - if (out_file != 0 && ~iq && (test_type==2 || test_type==3)) $fwrite(out_file," %d %d %d %d %d %d\n", setmp_d, setmp, out_xy_d, out_xy, m_err_scaling, p_err_scaling); - if (out_file != 0 && ~iq && test_type==4) $fwrite(out_file," %d %d %d %d %d %d\n", in1_d, in1, out_xy_d, out_xy, m_err_scaling, p_err_scaling); + if (out_file != 0 && ~iq && (test_type==2 || test_type==3)) + $fwrite(out_file," %d %d %d %d %d %d\n", setmp_d, setmp, out_xy_d, out_xy, m_err_scaling, p_err_scaling); + if (out_file != 0 && ~iq && test_type==4) + $fwrite(out_file," %d %d %d %d %d %d\n", in1_d, in1, out_xy_d, out_xy, m_err_scaling, p_err_scaling); if (sync_d) count_syncs <= count_syncs + 1'b1; end `endif diff --git a/cmoc/llrf_shell.v b/cmoc/llrf_shell.v index 379042604..41488b339 100644 --- a/cmoc/llrf_shell.v +++ b/cmoc/llrf_shell.v @@ -56,6 +56,7 @@ module llrf_shell( `AUTOMATIC_self ); +`undef AUTOMATIC_self `AUTOMATIC_decode @@ -103,13 +104,12 @@ timestamp ts(.clk(clk), .aux_trig(1'b0), .slow_op(slow_op), .slow_snap(slow_snap // changing other controls. tag_now shows the value of tag at the end-time of // the buffer, tag_old shows it at the begin-time of the buffer. Not perfect // because of non-boxcar filtering and sloppy pipelining. -`define SLOW_SR_LEN 7*16 -`define SLOW_SR_DATA { adc1_min, adc1_max, adc2_min, adc2_max, adc3_min, adc3_max, tag_now, tag_old } -parameter sr_length = `SLOW_SR_LEN; -reg [sr_length-1:0] slow_read=0; reg [7:0] tag_old=0; +parameter sr_length = 7*16; +wire [sr_length-1:0] slow_sr_data = { adc1_min, adc1_max, adc2_min, adc2_max, adc3_min, adc3_max, tag_now, tag_old }; +reg [sr_length-1:0] slow_read=0; always @(posedge clk) if (slow_op) begin - slow_read <= slow_snap ? `SLOW_SR_DATA : {slow_read[sr_length-9:0],timestamp_out}; + slow_read <= slow_snap ? slow_sr_data : {slow_read[sr_length-9:0],timestamp_out}; if (slow_snap) tag_old <= tag_now; end assign slow_out = slow_read[sr_length-1:sr_length-8]; diff --git a/cmoc/piezo_control.v b/cmoc/piezo_control.v index 92c670af7..0203e3550 100644 --- a/cmoc/piezo_control.v +++ b/cmoc/piezo_control.v @@ -23,6 +23,7 @@ module piezo_control( output [6:0] trace_en_addr // external address for trace_en //`AUTOMATIC_self ); +`undef AUTOMATIC_self // Non-zero placeholder // Eventually intend to use reg_mac2 or similar; diff --git a/cmoc/rf_controller.v b/cmoc/rf_controller.v index 07a2617d0..f54d21107 100644 --- a/cmoc/rf_controller.v +++ b/cmoc/rf_controller.v @@ -69,6 +69,7 @@ module rf_controller ( input [7:0] tag, // external `AUTOMATIC_self ); +`undef AUTOMATIC_self `AUTOMATIC_decode diff --git a/cmoc/rules.mk b/cmoc/rules.mk index 500267ab5..fe7d62f55 100644 --- a/cmoc/rules.mk +++ b/cmoc/rules.mk @@ -2,11 +2,11 @@ include $(CORDIC_DIR)/rules.mk include $(DSP_DIR)/lo_lut/rules.mk # be lazy about dependencies -VFLAGS += -y. -I. -y$(RTSIM_DIR) -y$(DSP_DIR) -y$(DSP_DIR)/hosted -y$(CORDIC_DIR) -y$(BADGER_DIR) -y_autogen +VFLAGS += -y. -I. -y$(RTSIM_DIR) -y$(DSP_DIR) -y$(DSP_DIR)/hosted -y$(CORDIC_DIR) -y$(BADGER_DIR) -y$(BEDROCK_DIR)/localbus -y_autogen LB_AW = 15 NEWAD_ARGS += -m -l -NEWAD_DIRS += $(DSP_DIR) $(DSP_DIR)/hosted $(RTSIM_DIR) $(CMOC_DIR) +NEWAD_DIRS += $(DSP_DIR) $(DSP_DIR)/hosted $(RTSIM_DIR) $(CMOC_DIR) $(BEDROCK_DIR)/localbus VERILOG_AUTOGEN += "cordicg_b22.v" diff --git a/cordic/README b/cordic/README index 243350588..f8d075d29 100644 --- a/cordic/README +++ b/cordic/README @@ -9,7 +9,7 @@ See LICENSE.md, enclosed. Good reference material on CORDIC hardware in general is given by Ray Andraka at - http://www.andraka.com/cordic.php + http://andraka.com/cordic.php and of course see the general and long-winded background at https://en.wikipedia.org/wiki/CORDIC diff --git a/doc/Makefile b/doc/Makefile index 1f2737bf4..91e1801ea 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -26,6 +26,10 @@ GEN_DIR = _gen_rst GEN_MD_DIR = _gen_md GEN_SRC_DIR = _gen_src_rst +# Works for now, but is an indication that our makefiles need work +VCD_ARGS = $(VCD_ARGS_$@) +VCD_ARGS_tgen.vcd = +tgen_seq=../localbus/tgen_seq.dat + # No need to specify directory for each of these, thanks to the magic of make's vpath. SRC = \ afterburner.v \ @@ -97,6 +101,7 @@ SRC = \ serializer_multichannel.v \ serialize.v \ shortfifo.v \ + tgen.v \ timestamp.v \ tt800.v \ upconv.v \ @@ -160,7 +165,7 @@ SRC_SVG = \ build-tools/cdc_OK1.svg \ build-tools/cdc_OKX.svg -DIRS = $(DSP_DIR) $(BADGER_DIR) $(CORDIC_DIR) $(PICORV_DIR) $(HOMELESS_DIR) $(SERIAL_IO_DIR)/EVG_EVR +DIRS = $(DSP_DIR) $(BADGER_DIR) $(CORDIC_DIR) $(PICORV_DIR) $(HOMELESS_DIR) $(SERIAL_IO_DIR)/EVG_EVR $(BEDROCK_DIR)/localbus VFLAGS += -y. -I. $(addprefix -y, $(DIRS)) $(addprefix -I, $(DIRS)) -y$(AUTOGEN_DIR) vpath %.v $(DIRS) @@ -302,5 +307,5 @@ clean:: include $(BUILD_DIR)/bottom_rules.mk CLEAN += $(SRC_RST) $(RST) $(BLOCK_EPS) $(BLOCK_PNG) $(TIMING_PNG) $(GEN_TB) $(addsuffix *, $(DEP_TB)) $(VCD) \ - cordicg_b22.v *_tb *.out + cordicg_b22.v *_tb *.out *.pdf CLEAN_DIRS += $(GEN_DIR) $(GEN_SRC_DIR) $(GEN_MD_DIR) diff --git a/doc/conf.py b/doc/conf.py index bf2fbbbe5..609a949f4 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -4,7 +4,7 @@ # # This file does only contain a selection of the most common options. For a # full list see the documentation: -# http://www.sphinx-doc.org/en/master/config +# https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- diff --git a/dsp/Makefile b/dsp/Makefile index f7c6e0aef..a557afd77 100644 --- a/dsp/Makefile +++ b/dsp/Makefile @@ -1,4 +1,5 @@ include ../dir_list.mk +VERILOG_AUTOGEN += "cordicg_b22.v" .PHONY: all all: targets include $(BUILD_DIR)/top_rules.mk diff --git a/dsp/cim_12x.v b/dsp/cim_12x.v index 072689fb9..280829a97 100644 --- a/dsp/cim_12x.v +++ b/dsp/cim_12x.v @@ -40,22 +40,28 @@ module cim_12x #( // Snapshots double-integrator outputs at times flagged by "sample", then shifts the results out on the next twelve cycles. wire signed [dw-1:0] s01; wire g01; wire signed [dw-1:0] s03; wire g03; -mon_2chan #(.dwi(16), .rwi(dw)) mon01(.clk(clk), .adcf(adca), .mcos(cosa), .msin(sina), .samp(sample), .s_in(s03), .s_out(s01), .g_in(g03), .g_out(g01), .reset(reset)); +mon_2chan #(.dwi(16), .rwi(dw)) mon01(.clk(clk), .adcf(adca), .mcos(cosa), .msin(sina), + .samp(sample), .s_in(s03), .s_out(s01), .g_in(g03), .g_out(g01), .reset(reset)); wire signed [dw-1:0] s05; wire g05; -mon_2chan #(.dwi(16), .rwi(dw)) mon03(.clk(clk), .adcf(adcb), .mcos(cosa), .msin(sina), .samp(sample), .s_in(s05), .s_out(s03), .g_in(g05), .g_out(g03), .reset(reset)); +mon_2chan #(.dwi(16), .rwi(dw)) mon03(.clk(clk), .adcf(adcb), .mcos(cosa), .msin(sina), + .samp(sample), .s_in(s05), .s_out(s03), .g_in(g05), .g_out(g03), .reset(reset)); wire signed [dw-1:0] s07; wire g07; -mon_2chan #(.dwi(16), .rwi(dw)) mon05(.clk(clk), .adcf(adcc), .mcos(cosa), .msin(sina), .samp(sample), .s_in(s07), .s_out(s05), .g_in(g07), .g_out(g05), .reset(reset)); +mon_2chan #(.dwi(16), .rwi(dw)) mon05(.clk(clk), .adcf(adcc), .mcos(cosa), .msin(sina), + .samp(sample), .s_in(s07), .s_out(s05), .g_in(g07), .g_out(g05), .reset(reset)); wire signed [dw-1:0] s09; wire g09; -mon_2chiq #(.dwi(16), .rwi(dw)) mon07(.clk(clk), .iqd(inm), .scale(scale), .iqs(iqs), .samp(sample), .s_in(s09), .s_out(s07), .g_in(g09), .g_out(g07), .reset(reset)); +mon_2chiq #(.dwi(16), .rwi(dw)) mon07(.clk(clk), .iqd(inm), .scale(scale), .iqs(iqs), + .samp(sample), .s_in(s09), .s_out(s07), .g_in(g09), .g_out(g07), .reset(reset)); wire signed [dw-1:0] s11; wire g11; -mon_2chiq #(.dwi(16), .rwi(dw)) mon09(.clk(clk), .iqd(outm), .scale(scale), .iqs(iqs), .samp(sample), .s_in(s11), .s_out(s09), .g_in(g11), .g_out(g09), .reset(reset)); +mon_2chiq #(.dwi(16), .rwi(dw)) mon09(.clk(clk), .iqd(outm), .scale(scale), .iqs(iqs), + .samp(sample), .s_in(s11), .s_out(s09), .g_in(g11), .g_out(g09), .reset(reset)); wire signed [dw-1:0] s13; wire g13; -mon_2chan #(.dwi(16), .rwi(dw)) mon11(.clk(clk), .adcf(adcx), .mcos(cosb), .msin(sinb), .samp(sample), .s_in(s13), .s_out(s11), .g_in(g13), .g_out(g11), .reset(reset)); +mon_2chan #(.dwi(16), .rwi(dw)) mon11(.clk(clk), .adcf(adcx), .mcos(cosb), .msin(sinb), + .samp(sample), .s_in(s13), .s_out(s11), .g_in(g13), .g_out(g11), .reset(reset)); // terminate the chain assign s13 = {dw{`FILL_BIT}}; diff --git a/dsp/digaree/detune_coeff_calc.py b/dsp/digaree/detune_coeff_calc.py index d52a47164..dcf5ba286 100644 --- a/dsp/digaree/detune_coeff_calc.py +++ b/dsp/digaree/detune_coeff_calc.py @@ -1,6 +1,6 @@ import numpy as np from numpy import sqrt, arctan2, exp, mean, std, pi, zeros, hstack, vstack, arange, diff, linalg -from scipy import signal +from scipy.interpolate import BSpline from matplotlib import pyplot @@ -196,16 +196,24 @@ class detune_pulse(digaree_coeff): """ Cardinal B-spline https://en.wikipedia.org/wiki/B-spline Same as the output of a second-order CIC interpolator + Bespoke wrapper as drop-in for the old scipy.signal.bspline, + which was removed in SciPy 1.13. """ + def bspline(self, x, n): + knots = np.arange(-(n+1)/2, (n+3)/2) + out = BSpline.basis_element(knots)(x) + out[(x < knots[0]) | (x > knots[-1])] = 0.0 + return out + def create_basist(self, block=15, n=10): npt = (n-1)*block+1 basist = zeros([npt, n]) x = arange(npt)/float(block) for jx in range(n): - basist[:, jx] = signal.bspline(x-jx, 2) + basist[:, jx] = self.bspline(x-jx, 2) # end-effects, it's important that the sum is flat - basist[:, 0] += signal.bspline(x+1, 2) - basist[:, n-1] += signal.bspline(x-n, 2) + basist[:, 0] += self.bspline(x+1, 2) + basist[:, n-1] += self.bspline(x-n, 2) return basist # basist is n*m, where n=len(cav)-1, m is number of time-dependent bases diff --git a/dsp/digaree/sched.py b/dsp/digaree/sched.py index 2916d650b..e47b79f09 100644 --- a/dsp/digaree/sched.py +++ b/dsp/digaree/sched.py @@ -19,7 +19,7 @@ def tobin(x, count=8): # Integer to binary; count is number of bits - # Credit to W.J. van der Laan in http://code.activestate.com/recipes/219300/ + # Credit to W.J. van der Laan in https://code.activestate.com/recipes/219300/ return "".join([str((x >> y) & 1) for y in range(count-1, -1, -1)]) diff --git a/dsp/feedforward/cic_bank_memgen.py b/dsp/feedforward/cic_bank_memgen.py index 38575ccfd..aa834f52d 100644 --- a/dsp/feedforward/cic_bank_memgen.py +++ b/dsp/feedforward/cic_bank_memgen.py @@ -94,7 +94,7 @@ def gen_array(pulse_vals, print_me=True): filln = 4*512 - len(pulse_vals) pulse_vals += [0] * filln - if (print_me): + if print_me: for x in pulse_vals: print(x) diff --git a/dsp/fifo_2c.v b/dsp/fifo_2c.v index c5451f530..0c1c5466a 100644 --- a/dsp/fifo_2c.v +++ b/dsp/fifo_2c.v @@ -3,7 +3,7 @@ // Based on, and mostly compatible with, OpenCores (Rudolf Usselmann) // generic_fifo_dc_gray (Universal FIFO Dual Clock, gray encoded), // downloaded from: -// http://www.opencores.org/cores/generic_fifos/ +// https://opencores.org/projects/generic_fifos // This version drops the rst and clr inputs, as befits my FPGA needs, // and neglects to provide wr_level and rd_level outputs. It is also // coded in a more compact style. diff --git a/dsp/filter_test.py b/dsp/filter_test.py index 89906bf02..6227d82e5 100644 --- a/dsp/filter_test.py +++ b/dsp/filter_test.py @@ -118,12 +118,12 @@ def check_tf(freq, gain, phase, tb_cfg, plot=False): fmeas = c_est fspec = tb_cfg["fcorner"] diff = (np.log10(fmeas) - np.log10(fspec))/np.log10(fspec) - if (abs(diff) > 0.1): + if abs(diff) > 0.1: print("FAIL: Measured 3dB corner (%.1f) too far from spec (%.1f)!" % (fmeas, fspec)) fail = True sspec = tb_cfg["slope"] diff = (slope - sspec)/sspec - if (abs(diff) > 0.1): + if abs(diff) > 0.1: print("FAIL: Measured slope (%.3f) too far from spec (%.3f)!" % (slope, sspec)) fail = True diff --git a/dsp/flag_xdomain.v b/dsp/flag_xdomain.v index 3f3fa3d45..34e92ae84 100644 --- a/dsp/flag_xdomain.v +++ b/dsp/flag_xdomain.v @@ -20,4 +20,19 @@ reg sync2_clk2=0; always @(posedge clk2) sync2_clk2 <= sync1_clk2; assign flagout_clk2 = sync2_clk2 ^ sync1_clk2; +// Step 4: (simulation-only) complain if this module is abused +// Too many people wire data_xdomain's .gate_in(1'b1) without understanding +// how the logic is supposed to work. +`ifdef SIMULATE +reg old_flagin=0, warning_sent=0; +always @(posedge clk1) begin + old_flagin <= flagin_clk1; + if (flagin_clk1 & old_flagin & ~warning_sent) begin + // Don't actually crash, at least not for now. + $display("XXXX Warning: flag_xdomain module abuse in %m XXXX"); + warning_sent <= 1; + end +end +`endif + endmodule diff --git a/dsp/freq_count.gtkw b/dsp/freq_count.gtkw index 26659afbd..6a16144bf 100644 --- a/dsp/freq_count.gtkw +++ b/dsp/freq_count.gtkw @@ -3,11 +3,12 @@ [pos] -1 -1 *-6.533834 3870 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] freq_count_tb. -[sst_width] 210 +[sst_width] 213 [signals_width] 174 [sst_expanded] 1 [sst_vpaned_height] 154 @28 +freq_count_tb.clk freq_count_tb.f_in @22 freq_count_tb.diff_stream[15:0] @@ -15,12 +16,14 @@ freq_count_tb.diff_stream[15:0] freq_count_tb.diff_stream_strobe @24 freq_count_tb.frequency[27:0] +@28 +freq_count_tb.mut.freq_strobe @22 freq_count_tb.mut.work.gray1[3:0] freq_count_tb.mut.work.gray3[3:0] @24 freq_count_tb.mut.work.diff1[3:0] -freq_count_tb.mut.work.refcnt[5:0] +freq_count_tb.mut.refcnt[5:0] freq_count_tb.mut.work.accum[27:0] [pattern_trace] 1 [pattern_trace] 0 diff --git a/dsp/freq_count.v b/dsp/freq_count.v index d07a5bd61..919bb3c32 100644 --- a/dsp/freq_count.v +++ b/dsp/freq_count.v @@ -4,7 +4,7 @@ `timescale 1ns / 1ns module freq_count #( - // Default configuration useful for input frequencies < 96 MHz + // Default configuration useful for input frequencies < 2*sysclk parameter glitch_thresh=2, parameter refcnt_width=24, parameter freq_width=28, @@ -15,26 +15,65 @@ module freq_count #( input f_in, // unknown input // outputs in sysclk domain - output [freq_width-1:0] frequency, + output reg [freq_width-1:0] frequency, output freq_strobe, - output [15:0] diff_stream, - output diff_stream_strobe, + output reg [15:0] diff_stream, // stream of last 4 4-bit counts of f_in + output reg diff_stream_strobe, // strobe at f_sysclk/4 // glitch_catcher can be routed to a physical pin to trigger // a 'scope; see glitch_thresh parameter above - output glitch_catcher + output reg glitch_catcher ); +initial begin + frequency=initv; + diff_stream=0; + diff_stream_strobe=0; + glitch_catcher=0; +end + +// Reference counter +// may or may not be synchronized between instances +reg [refcnt_width-1:0] refcnt=0; +reg ref_strobe=0, stream_strobe=0; +always @(posedge sysclk) begin + {ref_strobe, refcnt} <= refcnt + 1; + stream_strobe <= refcnt[1:0] == 0; +end + +wire [3:0] xcount; // per-sysclk count of f_in edges +wire [freq_width-1:0] frequency_w; freq_gcount #( - .glitch_thresh(glitch_thresh), - .refcnt_width(refcnt_width), + .gw(4), .freq_width(freq_width), .initv(initv) ) work ( .sysclk(sysclk), .f_in(f_in), .g_in(1'b1), // this is the whole point! - .frequency(frequency), .freq_strobe(freq_strobe), - .diff_stream(diff_stream), .diff_stream_strobe(diff_stream_strobe), - .glitch_catcher(glitch_catcher) + .ref_strobe(ref_strobe), + .frequency(frequency_w), .freq_strobe(freq_strobe), + .xcount(xcount) ); +// Nobody except some ancient USB debugging ever used this. +// It's harmless; if you don't attach to the diff_stream, +// diff_stream_strobe, or glitch_catcher ports, it will all +// just disappear in synthesis. +// +// Make xcount available to stream to host at 24 MByte/sec, which was +// especially interesting when reprogramming a AD9512 clock divider +// on a LLRF4 board. +// It might also be possible to histogram xcount. +reg [15:0] stream=0; +always @(posedge sysclk) begin + if (xcount > glitch_thresh) glitch_catcher <= ~glitch_catcher; + stream <= {stream[11:0], xcount}; // assumes freq_gcount gw=4 +end + +// Latch/pipeline one more time to perimeter of this module +always @(posedge sysclk) begin + diff_stream <= stream; + diff_stream_strobe <= stream_strobe; + frequency <= frequency_w; +end + endmodule diff --git a/dsp/freq_count_tb.v b/dsp/freq_count_tb.v index 1830deb0f..3cf0fad99 100644 --- a/dsp/freq_count_tb.v +++ b/dsp/freq_count_tb.v @@ -16,7 +16,8 @@ initial begin end // Simulated accumulation interval is 20*64 = 1280 ns // Should catch an average of 1280/6 = 213.33 f_in edges in that time - if (frequency>212 && frequency < 215) begin + $display("frequency = %d", frequency); + if (frequency > 212 && frequency < 215) begin $display("PASS"); $finish(0); end else begin diff --git a/dsp/freq_gcount.v b/dsp/freq_gcount.v index e403c13e9..183130d9e 100644 --- a/dsp/freq_gcount.v +++ b/dsp/freq_gcount.v @@ -1,40 +1,39 @@ // Copied from the old freq_count, but with new g_in (gate) input added // Read the new name as frequency (gated) count. +// +// This version delegates reference counter function to the caller, +// to allow synchronization and resource-sharing across multiple +// freq_gcount instances. Note that if you want a _lot_ of frequency +// counter instances, maybe you should look into multi_counter.v. +// +// It no longer includes the obscure glitch diagnostics. Those are +// still available in freq_count, just in case anyone wants them. `timescale 1ns / 1ns module freq_gcount #( - // Default configuration useful for input frequencies < 96 MHz - parameter glitch_thresh=2, - parameter refcnt_width=24, + parameter gw=4, // Gray code counter width parameter freq_width=28, - parameter initv=0 + parameter initv=0 // output value for frequency at start-up ) ( // input clocks input sysclk, // timespec 8.0 ns input f_in, // unknown input - input g_in, // gate (f_in clock domain) + + // control input in f_in domain + input g_in, // gate (wire to 1 to get a simple frequency counter) + + // control input in sysclk domain + input ref_strobe, // typically one pulse every 2^24 sysclk cycles // outputs in sysclk domain - output reg [freq_width-1:0] frequency, + output [freq_width-1:0] frequency, output freq_strobe, - output reg [15:0] diff_stream, - output reg diff_stream_strobe, - // glitch_catcher can be routed to a physical pin to trigger - // a 'scope; see glitch_thresh parameter above - output reg glitch_catcher + output [gw-1:0] xcount // cycle-by-cycle gated count of f_in ); -initial begin - frequency=initv; - diff_stream=0; - diff_stream_strobe=0; - glitch_catcher=0; -end - -// four-bit Gray code counter on the input signal -// http://en.wikipedia.org/wiki/Gray_code -localparam gw=4; +// four-bit (nominal) Gray code counter on the input signal +// https://en.wikipedia.org/wiki/Gray_code reg [gw-1:0] gray1=0; // The following three expressions compute the next Gray code based on @@ -57,7 +56,7 @@ end // verilator lint_save // verilator lint_off UNOPTFLAT -wire [gw-1:0] bin3 = gray3 ^ {1'b0, bin3[gw-1:1]}; // convert Gray to binary +wire [gw-1:0] bin3 = gray3 ^ {1'b0, bin3[gw-1:1]}; // Gray to binary // verilator lint_restore reg [gw-1:0] bin4=0, bin5=0, diff1=0; @@ -65,37 +64,19 @@ always @(posedge sysclk) begin bin4 <= bin3; bin5 <= bin4; diff1 <= bin4-bin5; - if (diff1 > glitch_thresh) glitch_catcher <= ~glitch_catcher; end -// It might also be possible to histogram diff1, -// but for now just accumulate it to get a traditional frequency counter. -// Also make it available to stream to host at 24 MByte/sec, might be -// especially interesting when reprogramming a clock divider like -// the AD9512 on a LLRF4 board. -// In that case a 48 MHz sysclk / 2^24 = 2.861 Hz update +// Accumulate diff1 to get a traditional frequency counter reg [freq_width-1:0] accum=0, result=initv; -reg [refcnt_width-1:0] refcnt=0; -reg ref_carry=0; -reg [15:0] stream=0; -reg stream_strobe=0; -always @(posedge sysclk) begin - {ref_carry, refcnt} <= refcnt + 1; - if (ref_carry) result <= accum; - accum <= (ref_carry ? 28'b0 : accum) + diff1; - stream <= {stream[11:0],diff1}; - stream_strobe <= refcnt[1:0] == 0; -end - -// Latch/pipeline one more time to perimeter of this module -// to make routing easier reg freq_strobe_r=0; always @(posedge sysclk) begin - frequency <= result; - freq_strobe_r <= ref_carry; - diff_stream <= stream; - diff_stream_strobe <= stream_strobe; + accum <= (ref_strobe ? {freq_width{1'b0}} : accum) + diff1; + if (ref_strobe) result <= accum; + freq_strobe_r <= ref_strobe; // high when new data is valid end + +assign frequency = result; // Don't over-register assign freq_strobe = freq_strobe_r; +assign xcount = diff1; endmodule diff --git a/dsp/hosted/etrig_bridge.gtkw b/dsp/hosted/etrig_bridge.gtkw new file mode 100644 index 000000000..52bf807c2 --- /dev/null +++ b/dsp/hosted/etrig_bridge.gtkw @@ -0,0 +1,28 @@ +[timestart] 0 +[size] 1166 445 +[pos] 763 125 +*-23.377670 3480000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] etrig_bridge_tb. +[treeopen] etrig_bridge_tb.UUT. +[sst_width] 233 +[signals_width] 198 +[sst_expanded] 1 +[sst_vpaned_height] 93 +@28 +etrig_bridge_tb.UUT.adc_clk +etrig_bridge_tb.UUT.lb_clk +etrig_bridge_tb.UUT.trign_0 +etrig_bridge_tb.UUT.trign_1 +@29 +etrig_bridge_tb.UUT.trign_2 +@28 +etrig_bridge_tb.UUT.sel[1:0] +@24 +etrig_bridge_tb.UUT.period[25:0] +etrig_bridge_tb.UUT.delay[25:0] +etrig_bridge_tb.etrig_pulse_cnt[15:0] +@28 +etrig_bridge_tb.UUT.etrig_pulse +etrig_bridge_tb.UUT.etrig_pulse_delayed +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/dsp/hosted/etrig_bridge.v b/dsp/hosted/etrig_bridge.v new file mode 100644 index 000000000..8fa0459db --- /dev/null +++ b/dsp/hosted/etrig_bridge.v @@ -0,0 +1,134 @@ +// Module that manages the asynchronous external trigger, and the internally-generated periodic trigger. +// Performs clock-domain synchronization, and delays the assertion of the trigger accordingly. +// ---------- + +module etrig_bridge ( + // Two clocks, ADC clock and local clock + input lb_clk, + input adc_clk, + // Three external triggers, active-low + input trign_0, + input trign_1, + input trign_2, + // Control registers + input [1:0] sel, // external + input [25:0] period, // external + input [25:0] delay, // external + // Trigger Counter + output [15:0] etrig_pulse_cnt, + // Trigger Outputs + output etrig_pulse, + output etrig_pulse_delayed +); + + // ------- + // Multi-board synchronization w/ selectable external or programmable trigger + // ------- + localparam ETRIG_0 = 0, + ETRIG_1 = 1, + ETRIG_2 = 2, + ETRIG_PROG = 3; + + // wires and regs + reg [25:0] etrig_p_cnt = 0; // counter for internally-generated trigger + wire etrig_p_pulse, etrig_p_pulse_x; // the internal trigger and its synced partner + wire async_trig, etrig; // MUX outputs + reg [6:0] async_filt_cnt = 0; // to filter-out glitches in the external trigger + wire async_trig_filt; // filtered-out trigger + reg etrig_r = 0, etrig_r1 = 0, etrig_pulse_i = 0; // for edge detection + reg [1:0] etrig_sreg = 0; // shift register to ensure proper delay + reg [15:0] etrig_pulse_cnt_i = 0; // count the triggers that are issued + reg [25:0] delay_cnt = 0; // delay counter + reg etrig_pulse_delayed_i = 0; // delayed pulse + reg etrig_toggle = 0; // flag indicating that we received a pulse + + // 2-FF synchronizers + (* ASYNC_REG = "TRUE" *) reg [1:0] trign_0_sync=0, trign_1_sync=0, trign_2_sync=0; + + // Move delay control to adc_clk domain. Should be attainable using newad. + reg [25:0] delay_r=0; always @(posedge adc_clk) delay_r <= delay; + + // generates the internal trigger + always @(posedge lb_clk) begin + etrig_p_cnt <= (etrig_p_cnt == 0) ? period : etrig_p_cnt - 1; + end + + assign etrig_p_pulse = (etrig_p_cnt == 1); + + // from lb to adc clock domain + flag_xdomain i_flagx_etrig ( + .clk1(lb_clk), .flagin_clk1(etrig_p_pulse), + .clk2(adc_clk), .flagout_clk2(etrig_p_pulse_x)); + + // 2-FF synchronizer before handling further + reg [1:0] sel_r=0; + always @(posedge adc_clk) begin + trign_0_sync <= {trign_0_sync[0], trign_0}; + trign_1_sync <= {trign_1_sync[0], trign_1}; + trign_2_sync <= {trign_2_sync[0], trign_2}; + sel_r <= sel; // arrived in lb_clk + end + + // MUX to decide which trigger to propagate (external triggers are active-low) + assign async_trig = (sel_r == ETRIG_0) ? ~trign_0_sync[1] : + (sel_r == ETRIG_1) ? ~trign_1_sync[1] : + (sel_r == ETRIG_2) ? ~trign_2_sync[1] : + 1'b0; + + // Glitch filter async inputs by ignoring pulses shorter than 128/adc_clk = 1.356 us + always @(posedge adc_clk) begin + if (async_trig == 0) async_filt_cnt <= 0; + else if (!async_trig_filt) async_filt_cnt <= async_filt_cnt + 1; + end + assign async_trig_filt = &(async_filt_cnt); + + assign etrig = (sel_r == ETRIG_PROG) ? etrig_p_pulse_x : async_trig_filt; + + // Rising-edge detect + always @(posedge adc_clk) begin + etrig_r <= etrig; + etrig_r1 <= etrig_r; + etrig_pulse_i <= etrig_r & ~etrig_r1; + if (etrig_pulse_i) + etrig_pulse_cnt_i <= etrig_pulse_cnt_i + 1; + end + + // Delay the trigger strobe + always @(posedge adc_clk) begin + if (etrig_pulse_i == 1'b1) begin + delay_cnt <= 0; // reset + etrig_toggle <= 1'b1; // raise the flag + etrig_pulse_delayed_i <= 1'b0; // not ready yet + end + else if (etrig_toggle == 1'b1 && delay_cnt < delay_r) begin + delay_cnt <= delay_cnt + 1; // keep rolling + etrig_toggle <= etrig_toggle; // retain + etrig_pulse_delayed_i <= 1'b0; // not ready yet + end + else if (etrig_toggle == 1'b1 && delay_cnt == delay_r) begin + delay_cnt <= 0; // reset + etrig_toggle <= 1'b0; // reset + etrig_pulse_delayed_i <= 1'b1; // one-clock pulse + end + else begin + delay_cnt <= 0; // reset + etrig_toggle <= 1'b0; // keep low + etrig_pulse_delayed_i <= 1'b0; // keep low + end + end + + // two-bit shift-register + // ensures one-cycle delay between trigger and delayed trigger if delay == 1 + always @(posedge adc_clk) begin + etrig_sreg <= etrig_sreg << 1; + etrig_sreg[0] <= etrig_pulse_i; + end + + // assigning to output ports + assign etrig_pulse_cnt = etrig_pulse_cnt_i; + assign etrig_pulse = etrig_sreg[1]; + + // mux for the case that the delay is zero + assign etrig_pulse_delayed = (delay_r == 0) ? etrig_sreg[1] : etrig_pulse_delayed_i; + +endmodule diff --git a/dsp/hosted/etrig_bridge_tb.v b/dsp/hosted/etrig_bridge_tb.v new file mode 100644 index 000000000..1e030bc16 --- /dev/null +++ b/dsp/hosted/etrig_bridge_tb.v @@ -0,0 +1,83 @@ +// etrig_bridge_tb.v + +`timescale 1 ns/10 ps // time-unit = 1 ns, precision = 10 ps + +module etrig_bridge_tb; + + reg j5_24v = 1, j4_24v = 1, de9_rxd = 1, de9_dsr = 1; // Active-low + reg [1:0] etrig_sel = 0; + reg [25:0] etrig_period = 26'h0000ff; + reg [25:0] etrig_delay = 26'h000000; + wire [15:0] etrig_pulse_cnt; + wire etrig_pulse; + wire etrig_pulse_delayed; + + // Testbench parameters and clocking + reg fail = 0; + integer seed_int=123; + localparam SIM_TIME = 50000; // ns + localparam period_lb_clk = 20; + localparam period_adc_clk = 10; + + reg free_lb_clk=0; + wire lb_clk; + always begin free_lb_clk = ~free_lb_clk; #(period_lb_clk/2); end + + reg free_adc_clk=0; + wire adc_clk; + always begin free_adc_clk = ~free_adc_clk; #(period_adc_clk/2); end + + reg pll_lock_emu=0; + + // Testbench control + initial begin + if ($test$plusargs("vcd")) begin + $dumpfile("etrig_bridge.vcd"); + $dumpvars(5, etrig_bridge_tb); + end + + while ($time < SIM_TIME) @(posedge lb_clk); + if (!fail) begin + $display("WARNING: Not a self-checking testbench. Will always PASS."); + $finish(0); + end else begin + $display("FAIL"); + $stop(0); + end + end + + etrig_bridge UUT ( + .lb_clk(lb_clk), .adc_clk(adc_clk), + .trign_0(j5_24v), + .trign_1(j4_24v), + .trign_2(de9_rxd&de9_dsr), + .sel(etrig_sel), .period(etrig_period), .delay(etrig_delay), + .etrig_pulse_cnt(etrig_pulse_cnt), .etrig_pulse(etrig_pulse), + .etrig_pulse_delayed(etrig_pulse_delayed) + ); + +// trigger loop +always begin + if (!pll_lock_emu) begin + #(($urandom(seed_int)%20)*period_lb_clk); + pll_lock_emu <= 1'b1; + end + + #(($urandom(seed_int)%20)*20); + {j4_24v, j5_24v, de9_rxd, de9_dsr} <= $urandom(seed_int)%16; + @(posedge adc_clk); + + #(($urandom(seed_int)%20)*200); + {j4_24v, j5_24v, de9_rxd, de9_dsr} <= (1<<4)-1; + etrig_delay <= etrig_delay + 1; + @(posedge adc_clk); +end + +always @(posedge adc_clk) + etrig_sel <= etrig_sel + etrig_pulse; + +// clocking +assign lb_clk = (pll_lock_emu) ? free_lb_clk : 1'b0; +assign adc_clk = (pll_lock_emu) ? free_adc_clk : 1'b0; + +endmodule diff --git a/dsp/hosted/lp_2notch.v b/dsp/hosted/lp_2notch.v index cb1d228e3..c03c501b6 100644 --- a/dsp/hosted/lp_2notch.v +++ b/dsp/hosted/lp_2notch.v @@ -13,6 +13,7 @@ module lp_2notch( output signed [19:0] y, `AUTOMATIC_self ); +`undef AUTOMATIC_self `AUTOMATIC_decode diff --git a/dsp/hosted/lp_notch.v b/dsp/hosted/lp_notch.v index e59a6425e..60197a02d 100644 --- a/dsp/hosted/lp_notch.v +++ b/dsp/hosted/lp_notch.v @@ -16,6 +16,7 @@ module lp_notch( output signed [19:0] y, `AUTOMATIC_self ); +`undef AUTOMATIC_self `AUTOMATIC_decode diff --git a/dsp/hosted/lp_notch_test.py b/dsp/hosted/lp_notch_test.py index 11094f076..64fbe18e4 100644 --- a/dsp/hosted/lp_notch_test.py +++ b/dsp/hosted/lp_notch_test.py @@ -97,7 +97,7 @@ def __init__( Abp_zn = cl0_bp.response(zn) # solve for the final gains mat = numpy.array([[Alp_dc, Abp_dc], [Alp_zn, Abp_zn]]) - target_gains = numpy.array([[self.gain], [self.ngain]]) + target_gains = numpy.array([self.gain, self.ngain]) raw_gains = numpy.linalg.inv(mat).dot(target_gains) # prevent the low-pass component from clipping if abs(raw_gains[0]) > 0.99: diff --git a/dsp/hosted/non_iq_interleaved_piloop.v b/dsp/hosted/non_iq_interleaved_piloop.v index 11a15a804..86cb77107 100644 --- a/dsp/hosted/non_iq_interleaved_piloop.v +++ b/dsp/hosted/non_iq_interleaved_piloop.v @@ -16,7 +16,7 @@ // + // K_i/K_p // -// Created with http://asciiflow.com +// Created with https://asciiflow.com // DELAY: 9 cycles of delay at @clk `timescale 1ns / 1ns diff --git a/dsp/hosted/rules.mk b/dsp/hosted/rules.mk index 5bd1cbdb3..390312a32 100644 --- a/dsp/hosted/rules.mk +++ b/dsp/hosted/rules.mk @@ -4,7 +4,7 @@ VFLAGS_DEP += -I. -y . -y$(DSP_DIR) -y$(CORDIC_DIR) VFLAGS += -I. -y . -y$(DSP_DIR) -y$(CORDIC_DIR) -I$(AUTOGEN_DIR) -TEST_BENCH = lp_tb lp_2notch_tb lp_notch_tb phs_avg_tb mp_proc_tb +TEST_BENCH = lp_tb lp_2notch_tb lp_notch_tb phs_avg_tb mp_proc_tb etrig_bridge_tb TGT_ := $(TEST_BENCH) diff --git a/dsp/ph_acc.v b/dsp/ph_acc.v index a696666b6..7d1172816 100644 --- a/dsp/ph_acc.v +++ b/dsp/ph_acc.v @@ -4,6 +4,43 @@ // Tuned to allow 32-bit control, divided 20-bit high and 12-bit low, // which gets merged to 32-bit binary when modulo is zero. // But also supports non-binary frequencies: see the modulo input port. + +// Note that phase_step_h and phase_step_l combined fit in a 32-bit word. +// This is intentional, to allow atomic updates of the two controls +// in 32-bit systems. Indeed, when modulo==0, those 32 bits can be considered +// a single phase step increment + +// The phase generation algorithm +// 0. The phase increments for dds are generated using a technique described +// in these 2 places: +// Section: PROGRAMMABLE MODULUS MODE in: +// https://www.analog.com/media/en/technical-documentation/data-sheets/ad9915.pdf +// (AND) https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm +// Basically, increment the phase step at a coarse resolution, accumulate the +// error on the side, and when that error accumulates to the lowest bit of +// the coarse counter, add an extra 1 to the following phase step. +// 1. phase_step_h is the coarse (20 bit) integer truncated phase increment for +// the cordic. There is a 1-bit increment of phase that comes from +// accumulating residue phase. +// 2. This residue phase is accumulating in steps of phase_step_l, in a 12-bit +// counter. +// 3. However, there will be an extra residue even for this 12-bit counter, +// which is the modulus, and this added as an offset when the counter crosses 0 + +// 12-bit modulo supports largest known periodicity in a suggested LLRF system, +// 1427 for JLab. For more normal periodicities, use a multiple to get finer +// granularity. +// Note that the downloaded modulo control is the 2's complement of the +// mathematical modulus. +// e.g., SSRF IF/F_s ratio 8/11, use +// modulo = 4096 - 372*11 = 4 +// phase_step_h = 2^20*8/11 = 762600 +// phase_step_l = (2^20*8%11)*372 = 2976 +// e.g., Argonne RIA test IF/F_s ratio 9/13, use +// modulo = 4096 - 315*13 = 1 +// phase_step_h = 2^20*9/13 = 725937 +// phase_step_l = (2^20*9%13)*315 = 945 + module ph_acc( input clk, // Rising edge clock input; all logic is synchronous in this domain input reset, // Active high, synchronous with clk diff --git a/dsp/phase_diff.gtkw b/dsp/phase_diff.gtkw new file mode 100644 index 000000000..589bda85c --- /dev/null +++ b/dsp/phase_diff.gtkw @@ -0,0 +1,33 @@ +[timestart] 0 +[size] 1150 707 +[pos] -1 -1 +*-23.233957 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] phase_diff_tb. +[treeopen] phase_diff_tb.track. +[sst_width] 269 +[signals_width] 219 +[sst_expanded] 1 +[sst_vpaned_height] 191 +@8024 +phase_diff_tb.phase_diff[15:0] +@20000 +- +@420 +phase_diff_tb.vfreq_out[16:0] +@200 +-trackers +@8024 +phase_diff_tb.track.phaset_out1[16:0] +@20000 +- +@28 +phase_diff_tb.track.fault1 +@8024 +phase_diff_tb.track.phaset_out2[16:0] +@20000 +- +@28 +phase_diff_tb.track.fault2 +phase_diff_tb.track.tick +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/dsp/phase_diff.v b/dsp/phase_diff.v index 4217a6e2e..949fc3592 100644 --- a/dsp/phase_diff.v +++ b/dsp/phase_diff.v @@ -8,22 +8,24 @@ module phase_diff #( parameter order2=1, parameter dw=14, parameter adv=3861, - parameter delta=33, - parameter ext_div1_en=0, - parameter ext_div2_en=0 + parameter delta=16 + ) ( input uclk1, // unknown clock 1 - input ext_div1, // external divider on uclk1 input uclk2, // unknown clock 2 - input ext_div2, // external divider on uclk2 + input uclk2g, input sclk, // sampling clock input rclk, // readout clock (data transfer, local bus) + output err, // the following are in rclk domain - output dval, output [dw-2:0] phdiff_out, output [dw-1:0] vfreq_out, - output locked + output err_ff ); +// For the default dw=14 case, the previous 32-bit status word +// (fully in the rclk domain) can be constructed externally as +// wire [31:0] status_out = {err_ff, vfreq_out, 4'b0, phdiff_out}; +// 32 = 1 + 14 + 4 + 13 // XXX vfreq_out could have extra msbs added by unwrapping, or better still, // adding higher-order bits to phaset. @@ -31,51 +33,41 @@ module phase_diff #( // Two phase trackers wire [dw-1:0] phaset_out1, phaset_out2; wire fault1, fault2; -phaset #( - .ext_div_en(ext_div1_en), - .order(order1), .dw(dw), .adv(adv), .delta(delta) -) track1( - .uclk(uclk1), - .sclk(sclk), - .ext_div(ext_div1), - .phase(phaset_out1), - .fault(fault1)); - -phaset #( - .ext_div_en(ext_div2_en), - .order(order2), .dw(dw), .adv(adv), .delta(delta) -) track2 ( - .uclk(uclk2), - .sclk(sclk), - .ext_div(ext_div2), - .phase(phaset_out2), - .fault(fault2)); +phaset #(.order(order1), .dw(dw), .adv(adv), .delta(delta)) track1( + .uclk(uclk1), .uclkg(1'b1), .sclk(sclk), + .phase(phaset_out1), .fault(fault1)); +phaset #(.order(order2), .dw(dw), .adv(adv), .delta(delta)) track2( + .uclk(uclk2), .uclkg(uclk2g), .sclk(sclk), + .phase(phaset_out2), .fault(fault2)); // Don't assume sclk phase is stable, just report the difference. // msb of phaset_out* is useless, just represents internal divider state; // that msb will be dropped in the pass through data_xdomain below. reg [dw-1:0] ph_diff_sclk=0; reg [dw-1:0] ph_sum=0, ph_sum_old=0, vernier_freq=0; -reg [10:0] cnt=0; +reg [9:0] cnt=0; wire tick = &cnt; wire fault = fault1 | fault2; -reg err_r=1; +reg err_r=0; always @(posedge sclk) begin ph_diff_sclk <= phaset_out1 - phaset_out2; ph_sum <= phaset_out1 + phaset_out2; cnt <= cnt+1; if (tick | fault) err_r <= fault; - if (tick & locked) begin + if (tick) begin ph_sum_old <= ph_sum; vernier_freq <= ph_sum - ph_sum_old; end end -assign locked = ~err_r; // no fancy clock domain crossing +assign err = err_r; // no fancy clock domain crossing // Periodically pass the result to rclk domain -wire din_stb = &cnt[4:0] && locked; -data_xdomain #(.size(dw+dw-1)) xdom( - .clk_in(sclk), .gate_in(din_stb), .data_in({ph_diff_sclk[dw-2:0], vernier_freq}), - .clk_out(rclk),.gate_out(dval), .data_out({phdiff_out, vfreq_out})); +data_xdomain #(.size(dw-1)) xdom1( + .clk_in(sclk), .gate_in(&cnt[4:0]), .data_in(ph_diff_sclk[dw-2:0]), + .clk_out(rclk), .data_out(phdiff_out)); +data_xdomain #(.size(dw)) xdom2( // inefficient + .clk_in(sclk), .gate_in(&cnt[4:0]), .data_in(vernier_freq), + .clk_out(rclk), .data_out(vfreq_out)); +reg_tech_cdc err_cdc(.I(err_r), .C(rclk), .O(err_ff)); endmodule diff --git a/dsp/phasex_tb.v b/dsp/phase_diff_tb.v similarity index 83% rename from dsp/phasex_tb.v rename to dsp/phase_diff_tb.v index df63d277b..0f42c03a9 100644 --- a/dsp/phasex_tb.v +++ b/dsp/phase_diff_tb.v @@ -1,6 +1,6 @@ `timescale 1ns / 1ps -module phasex_tb; +module phase_diff_tb; parameter FREQ1 = 238.0; // MHz parameter F_RATIO = 2; @@ -25,14 +25,14 @@ wire signed [DW-1:0] phase_diff; initial begin if ($test$plusargs("vcd")) begin - $dumpfile("phasex.vcd"); - $dumpvars(5,phasex_tb); + $dumpfile("phase_diff.vcd"); + $dumpvars(5,phase_diff_tb); end for (cc=0; cc=16 + if (&track.cnt[4:0]) begin phase_err = phase_diff - phase_expect; phase_pass = $abs(phase_err)/FULL_RANGE < err_bar; // $display("cc: %d, phase_err: %d, pass: %d", cc, phase_err, phase_pass); diff --git a/dsp/phaset.gtkw b/dsp/phaset.gtkw new file mode 100644 index 000000000..12212e5d3 --- /dev/null +++ b/dsp/phaset.gtkw @@ -0,0 +1,30 @@ +[timestart] 0 +[size] 1000 600 +[pos] 0 0 +*-25.448893 12500000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] phaset_tb. +[sst_width] 213 +[signals_width] 211 +[sst_expanded] 1 +[sst_vpaned_height] 154 +@28 +phaset_tb.track.div +phaset_tb.track.msb +phaset_tb.track.nsb +phaset_tb.track.move +phaset_tb.track.dn +phaset_tb.track.up +@8024 +phaset_tb.track.phase[13:0] +@20000 +- +@28 +phaset_tb.fault +@8420 +phaset_tb.phase_unw +@20000 +- +@28 +phaset_tb.fail +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/dsp/phaset.v b/dsp/phaset.v index 8ed93c7a1..49fc57420 100644 --- a/dsp/phaset.v +++ b/dsp/phaset.v @@ -11,12 +11,11 @@ module phaset #( parameter order=1, parameter dw=14, parameter adv=3861, - parameter delta=264, // 8*33 - parameter ext_div_en=0 + parameter delta=16 ) ( input uclk, + input uclkg, input sclk, - input ext_div, output [dw-1:0] phase, output fault // single cycle ); @@ -24,14 +23,12 @@ module phaset #( // _Still_ draw analogy to the AD9901 // Generalize to a Johnson counter reg [order-1:0] ishr=0; -always @(posedge uclk) ishr <= (ishr << 1) | {{order-1{1'b0}},~ishr[order-1]}; -wire div = ishr[0]; -//reg div=0; always @(posedge uclk) div <= ~div; +always @(posedge uclk) if (uclkg) ishr <= (ishr << 1) | {{order-1{1'b0}},~ishr[order-1]}; +wire capture; reg_tech_cdc capture_cdc(.I(ishr[0]), .C(sclk), .O(capture)); // Test bench fails for some initial phase_r values between 14900 and 15050. // In that case the fault output signals the problem. reg [dw-1:0] osc=0, acc=0, phase_r=0; -reg capture=0; reg fault_r=0; wire msb = acc[dw-1]; wire nsb = acc[dw-2]; @@ -41,7 +38,6 @@ wire dir = ~nsb; wire dn = move & dir; wire up = move & ~dir; always @(posedge sclk) begin - capture <= ext_div_en ? ext_div : div; if (move) phase_r <= phase_r + (dir ? -delta : delta); fault_r <= move & peak; osc <= osc + adv; diff --git a/dsp/phaset_tb.v b/dsp/phaset_tb.v new file mode 100644 index 000000000..5b3cfd698 --- /dev/null +++ b/dsp/phaset_tb.v @@ -0,0 +1,80 @@ +`timescale 1ns / 1ps + +module phaset_tb; + +// In real life, this clock is not coherent with the unknown clock +reg sclk; // 200 MHz +integer cc; +reg glitch; +reg fail=0; +initial begin + if ($test$plusargs("vcd")) begin + $dumpfile("phaset.vcd"); + $dumpvars(5,phaset_tb); + end + glitch = $test$plusargs("glitch"); + for (cc=0; cc<24000; cc=cc+1) begin + sclk=0; #2.5; + sclk=1; #2.5; + end + $display("%s", fail ? "FAIL" : "PASS"); + if (fail) $stop(0); + $finish(0); +end + +// 94.286 MHz + epsilon +// divide by 33 +// 174.9 +reg uclk1; +always begin + uclk1=0; #5.301; + uclk1=1; #5.301; +end + +// Device under test +wire [13:0] phaset_out; +wire fault; +// round(2**14*1320/14/2/200) = 3862 is the DDS frequency +// delta kinda sets the gain and resolution of the phase tracking loop +phaset #(.dw(14), .adv(3862), .delta(16)) track( + .uclk(uclk1), .uclkg(1'b1), .sclk(sclk), + .phase(phaset_out), .fault(fault)); + +// Demonstration that changing internal divider state (not the uclk1 phase +// itself) will also toggle the msb of phaset_out. That bit should therefore +// be ignored. To exercise this feature, +// make phaset_view VCD_ARGS_phaset.vcd=+glitch +initial @(cc==3000) if (glitch) track.ishr[0] = ~track.ishr[0]; + +// Unwrapped phase +integer phase_unw=0, phase_diff, phase0; +reg [13:0] phaset_d=0; +reg first=1, rate_ok; +real rate, rate_want=1.42199585; // see below +integer t0=3000, tlen=20000; +always @(posedge sclk) if (cc>1000) begin + phaset_d <= phaset_out; + phase_diff = phaset_out - phaset_d; + if (phase_diff < -8192) phase_diff = phase_diff + 16384; + if (~first && (phase_diff > 16) || (phase_diff < -16)) fail = 1; + if (first) phase_unw = phaset_out; + else phase_unw <= phase_unw + phase_diff; + if (fault & ~glitch) fail = 1; + if (cc==t0) phase0 = phase_unw; + if (cc==(t0+tlen+1)) begin + $display("Delta phase %d bits / %d cycles", + phase_unw - phase0, tlen); + rate = (phase_unw - phase0) * 1.0 / tlen; + rate_ok = $abs(rate - rate_want) < 0.001*rate_want; + $display("Rate %.5f bits/cycle vs. theory %.5f %s", + rate, rate_want, rate_ok ? " OK" : "BAD"); + if (!glitch && !rate_ok) fail = 1; + end + first = 0; +end +// 3862/2**14*200 MHz = 47.143555 MHz DDS "local oscillator" +// 1/(2*5.301 ns)/2 = 47.160913 MHz input to be measured +// 1/(2*5.301e-9)/2 - 3862/2**14*200e6 = 17358.35 Hz offset +// 17358.35 Hz * 16384 / 200 MHz = 1.42199585 bits/cycle + +endmodule diff --git a/dsp/phasex.gtkw b/dsp/phasex.gtkw deleted file mode 100644 index 4916c3813..000000000 --- a/dsp/phasex.gtkw +++ /dev/null @@ -1,59 +0,0 @@ -[timestart] 0 -[size] 1150 707 -[pos] -1 -1 -*-23.233957 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -[treeopen] phasex_tb. -[treeopen] phasex_tb.track. -[sst_width] 210 -[signals_width] 219 -[sst_expanded] 1 -[sst_vpaned_height] 191 -@28 -phasex_tb.trig -phasex_tb.ready -phasex_tb.done -phasex_tb.f -@24 -phasex_tb.raddr[5:0] -@22 -phasex_tb.dout[15:0] -@8024 -phasex_tb.phase_diff[12:0] -@20000 -- -@420 -phasex_tb.vfreq_out[13:0] -@200 --dut -@28 -phasex_tb.dut.invalid -phasex_tb.dut.run -phasex_tb.dut.ready_r -@22 -phasex_tb.dut.count[8:0] -@28 -phasex_tb.dut.div2 -phasex_tb.dut.div1 -@22 -phasex_tb.dut.shiftr[15:0] -@28 -phasex_tb.dut.wen -@22 -phasex_tb.dut.waddr[5:0] -@200 --trackers -@8024 -phasex_tb.track.phaset_out1[13:0] -@20000 -- -@28 -phasex_tb.track.fault1 -@8024 -phasex_tb.track.phaset_out2[13:0] -@20000 -- -@28 -phasex_tb.track.fault2 -phasex_tb.track.tick -[pattern_trace] 1 -[pattern_trace] 0 diff --git a/dsp/phasex.v b/dsp/phasex.v index e5ecfdf2b..6efc3084a 100644 --- a/dsp/phasex.v +++ b/dsp/phasex.v @@ -1,7 +1,7 @@ `timescale 1ns / 1ns // DMTD-inspired investigation into clock phasing -// No on-chip analysis, that could be added later once we see the captured patterns +// No on-chip analysis, but that could be added later once we see the captured patterns module phasex #( parameter aw=10 ) ( @@ -40,7 +40,7 @@ wire [aw-1:0] waddr = count[aw+2:3]; // Data flow logic, also in sclk domain reg [15:0] shiftr=0; -reg [1:0] snap=0; +(* ASYNC_REG = "TRUE" *) reg [1:0] snap=0; always @(posedge sclk) begin snap <= {div2, div1}; // safely crosses clock domain if (run) shiftr <= {shiftr[13:0], snap}; diff --git a/dsp/rot_dds.v b/dsp/rot_dds.v index 9ab9e12b0..613fd3713 100644 --- a/dsp/rot_dds.v +++ b/dsp/rot_dds.v @@ -10,40 +10,6 @@ // 8/11 for SSRF // 9/13 for Argonne RIA // -// The phase generation algorithm -// 0. The phase increments for dds are generated using a technique described -// in these 2 places: -// Section: PROGRAMMABLE MODULUS MODE in: -// https://www.analog.com/media/en/technical-documentation/data-sheets/ad9915.pdf -// (AND) https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm -// Basically, increment the phase step at a coarse resolution, accumulate the -// error on the side, and when that error accumulates to the lowest bit of -// the coarse counter, add an extra 1 to the following phase step. -// 1. phase_step_h is the coarse (20 bit) integer truncated phase increment for -// the cordic. There is a 1-bit increment of phase that comes from -// accumulating residue phase. -// 2. This residue phase is accumulating in steps of phase_step_l, in a 12-bit -// counter. -// 3. However, there will be an extra residue even for this 12-bit counter, -// which is the modulus, and this added as an offset when the counter crosses 0 - -// 12-bit modulo supports largest known periodicity in a suggested LLRF system, -// 1427 for JLab. For more normal periodicities, use a multiple to get finer -// granularity. -// Note that the downloaded modulo control is the 2's complement of the -// mathematical modulus. -// e.g., SSRF IF/F_s ratio 8/11, use -// modulo = 4096 - 372*11 = 4 -// phase_step_h = 2^20*8/11 = 762600 -// phase_step_l = (2^20*8%11)*372 = 2976 -// e.g., Argonne RIA test IF/F_s ratio 9/13, use -// modulo = 4096 - 315*13 = 1 -// phase_step_h = 2^20*9/13 = 725937 -// phase_step_l = (2^20*9%13)*315 = 945 - -// TODO: -// Potentially, isolate phase generation into a separate module. -// Haha, turns out there is ph_acc.v (We should USE it). // Synthesizes to ??? slices at ??? MHz in XC3Sxxx-4 using XST-?? // @@ -65,19 +31,18 @@ parameter lo_amp = 18'd79590; // Sometimes we cheat and use slightly smaller values than above, // to make other computations fit better. +wire [18:0] phase_acc; +ph_acc ph_acc_i ( + .clk(clk), .reset(reset), .en(1'b1), // input + .phase_acc(phase_acc), // output [18:0] + .phase_step_h(phase_step_h), // input [19:0] + .phase_step_l(phase_step_l), // input [11:0] + .modulo(modulo) // input [11:0] +); // See rot_dds_config -reg carry=0, reset_d=0; -reg [19:0] phase_h=0, phase_step_hp=0; -reg [11:0] phase_l=0; -always @(posedge clk) begin - {carry, phase_l} <= reset ? 13'b0 : ((carry ? modulo : 12'b0) + phase_l + phase_step_l); - phase_step_hp <= phase_step_h; - reset_d <= reset; - phase_h <= reset_d ? 20'b0 : (phase_h + phase_step_hp + carry); -end cordicg_b22 #(.nstg(20), .width(18), .def_op(0)) trig(.clk(clk), .opin(2'b00), - .xin(lo_amp), .yin(18'd0), .phasein(phase_h[19:1]), + .xin(lo_amp), .yin(18'd0), .phasein(phase_acc), // 2^17/1.64676 = 79594, use a smaller value to keep CORDIC round-off // from overflowing the output .xout(cosa), .yout(sina)); diff --git a/dsp/rules.mk b/dsp/rules.mk index bd6b21c8f..ae8631912 100644 --- a/dsp/rules.mk +++ b/dsp/rules.mk @@ -5,7 +5,7 @@ include $(CORDIC_DIR)/rules.mk VFLAGS_DEP += -I. -y . -y$(DSP_DIR) -y$(CORDIC_DIR) VFLAGS += -I. -y . -y$(DSP_DIR) -y$(CORDIC_DIR) -I$(AUTOGEN_DIR) -TEST_BENCH = data_xdomain_tb upconv_tb half_filt_tb complex_mul_tb tt800_tb rot_dds_tb mon_12_tb xy_pi_clip_tb iq_chain4_tb cordic_mux_tb timestamp_tb afterburner_tb ssb_out_tb banyan_tb banyan_mem_tb biquad_tb iirFilter_tb circle_buf_tb cic_multichannel_tb cic_wave_recorder_tb circle_buf_serial_tb iq_deinterleaver_tb serializer_multichannel_tb complex_freq_tb iq_trace_tb second_if_out_tb cpxmul_fullspeed_tb dpram_tb host_averager_tb cic_simple_us_tb phasex_tb complex_mul_flat_tb fwashout_tb lpass1_tb slew_xarray_tb isqrt_tb freq_count_tb +TEST_BENCH = data_xdomain_tb upconv_tb half_filt_tb complex_mul_tb tt800_tb rot_dds_tb mon_12_tb xy_pi_clip_tb iq_chain4_tb cordic_mux_tb timestamp_tb afterburner_tb ssb_out_tb banyan_tb banyan_mem_tb biquad_tb iirFilter_tb circle_buf_tb cic_multichannel_tb cic_wave_recorder_tb circle_buf_serial_tb iq_deinterleaver_tb serializer_multichannel_tb complex_freq_tb iq_trace_tb second_if_out_tb cpxmul_fullspeed_tb dpram_tb host_averager_tb cic_simple_us_tb phase_diff_tb phaset_tb complex_mul_flat_tb fwashout_tb lpass1_tb slew_xarray_tb isqrt_tb freq_count_tb TGT_ := $(TEST_BENCH) @@ -30,7 +30,7 @@ bits: $(BITS_) $(VVP) $< +trace $(PYTHON) $(word 2, $^) -f $* -rot_dds_tb: cordicg_b22.v +rot_dds_tb: cordicg_b22.v ph_acc.v mon_12_tb: cordicg_b22.v diff --git a/dsp/timestamp.v b/dsp/timestamp.v index 1f3a90a99..347c24db4 100644 --- a/dsp/timestamp.v +++ b/dsp/timestamp.v @@ -101,8 +101,8 @@ assign shift_in2 = aux_reg ? (apost8 ? ashiftd : axmit ? asnap_out : 0) : shift_ // Making an explicit copy like this avoids a warning when dw != 8 assign shift_out=snap_out; -// More-or-less equivalent to -// define SLOW_SR_DATA { time1, time2, time3, time4, atime1, atime2, atime3, atime4 } -// but uses far fewer resources +// More-or-less equivalent to adding +// { time1, time2, time3, time4, atime1, atime2, atime3, atime4 } +// to slow_sr_data, but uses far fewer resources endmodule diff --git a/dsp/via.v b/dsp/via.v index 5e4fdbba4..8f4eb964d 100644 --- a/dsp/via.v +++ b/dsp/via.v @@ -1,4 +1,4 @@ -//https://forums.xilinx.com/t5/Welcome-Join/synthesizable-verilog-connecting-inout-pins/td-p/284628 +// https://adaptivesupport.amd.com/s/question/0D52E00006iHrFnSAK/synthesizable-verilog-connecting-inout-pins?language=en_US /*module via (w, w) inout w; wire w; diff --git a/fpga_family/iserdes/lvds_dco.v b/fpga_family/iserdes/lvds_dco.v index b921d51cf..a248dd038 100644 --- a/fpga_family/iserdes/lvds_dco.v +++ b/fpga_family/iserdes/lvds_dco.v @@ -49,5 +49,11 @@ assign clk_div_to_bufg = clk_div_bufr; `endif BUFG bufg_i(.I(clk_div_to_bufg), .O(clk_div_bufg)); +`else +// Does almost nothing except establish some causality, +// and keep yosys from thinking this module is a white box. +assign clk_div_bufg = dco_p; +assign clk_div_bufr = dco_p; +assign dco_clk_out = dco_p; `endif endmodule diff --git a/fpga_family/iserdes/lvds_frame.v b/fpga_family/iserdes/lvds_frame.v index 126bc9925..c49c17792 100644 --- a/fpga_family/iserdes/lvds_frame.v +++ b/fpga_family/iserdes/lvds_frame.v @@ -6,5 +6,9 @@ module lvds_frame #(parameter flip_frame=0) ( `ifndef SIMULATE IBUFDS #(.DIFF_TERM("TRUE")) ibufds_frame(.I(flip_frame ? frame_n : frame_p), .IB(flip_frame ? frame_p : frame_n), .O(frame)); +`else +// Does almost nothing except establish some causality, +// and keep yosys from thinking this module is a white box. +assign frame = frame_p ^ flip_frame; `endif endmodule diff --git a/fpga_family/iserdes/lvds_iophy.v b/fpga_family/iserdes/lvds_iophy.v index 87a268882..2fdbe1b09 100644 --- a/fpga_family/iserdes/lvds_iophy.v +++ b/fpga_family/iserdes/lvds_iophy.v @@ -90,6 +90,11 @@ always @ (posedge clk_div) begin dout[7]<=flip_d?~Q8:Q8; end +`else +// Does almost nothing except establish some causality, +// and keep yosys from thinking this module is a white box. +always @ (posedge clk_div) dout <= {8{d_p}}; // horribly stupid +assign idelay_value_out = 0; // theoretically in clk_div domain, also `endif // SIMULATE endmodule diff --git a/fpga_family/mgt/qgt_wrap_stub.vh b/fpga_family/mgt/qgt_wrap_stub.vh index d171c99e5..82bf9ddec 100644 --- a/fpga_family/mgt/qgt_wrap_stub.vh +++ b/fpga_family/mgt/qgt_wrap_stub.vh @@ -151,6 +151,9 @@ `ifdef GT1_ENABLE gt1_txresetdone & gt1_rxresetdone, `else 1'b0, `endif `ifdef GT0_ENABLE gt0_txresetdone & gt0_rxresetdone `else 1'b0 `endif }; + // practice good preprocessor hygiene + `undef Q_GT_MODULE + `else // SIMULATE // Instantiate dummy components to help dependency generation and basic syntax checking diff --git a/fpga_family/spartan6/adc_cells.v b/fpga_family/spartan6/adc_cells.v index 835622962..633b6a7b8 100644 --- a/fpga_family/spartan6/adc_cells.v +++ b/fpga_family/spartan6/adc_cells.v @@ -41,7 +41,10 @@ module adc_cells( wire adc_iddr2_rst=1'b0; wire adc_iddr2_set=1'b0; wire adc_iddr2_ce=1'b1; - // IDDR2 primitive of sp6, refer to http://www.xilinx.com/support/documentation/sw_manuals/xilinx13_4/spartan6_hdl.pdf, page 56 for ddr_alignment + // IDDR2 primitive of Spartan-6 + // Refer to UG615 + // https://docs.amd.com/v/u/en-US/spartan6_hdl + // page 124 for ddr_alignment genvar ix; generate for (ix=0; ix $@ + $(PERL) $(BUILD_DIR)/ucf2par $(FPGA_FAMILY_DIR)/$(FPGA_FAMILY)/s6gtp.ucf > $@ CLEAN += s6_gtp_params.vh diff --git a/fpga_family/xilinx/IDDR.v b/fpga_family/xilinx/IDDR.v index 366c950d0..7d2cdbbcb 100644 --- a/fpga_family/xilinx/IDDR.v +++ b/fpga_family/xilinx/IDDR.v @@ -1,6 +1,6 @@ // trivial substitute for xilinx unisim models // for more info refer to -// https://www.xilinx.com/support/documentation/user_guides/ug471_7Series_SelectIO.pdf +// https://docs.amd.com/go/en-US/ug471_7Series_SelectIO module IDDR #( parameter DDR_CLK_EDGE="SAME_EDGE_PIPELINED" diff --git a/fpga_family/xilinx/MMCME2_BASE.v b/fpga_family/xilinx/MMCME2_BASE.v index 8af0a748c..28055ee55 100644 --- a/fpga_family/xilinx/MMCME2_BASE.v +++ b/fpga_family/xilinx/MMCME2_BASE.v @@ -56,6 +56,7 @@ module MMCME2_BASE #( assign CLKOUT0 = CLKIN1; assign CLKOUT1 = CLKIN1; +assign LOCKED = 1; // verilator lint_restore endmodule // MMCME2_BASE diff --git a/fpga_family/xilinx/ODDR.v b/fpga_family/xilinx/ODDR.v index 4d25db75c..8062451ce 100644 --- a/fpga_family/xilinx/ODDR.v +++ b/fpga_family/xilinx/ODDR.v @@ -1,5 +1,5 @@ // pathetic model of Xilinx DDR output cell -// ignores set and reset inputs +// ignores set and reset inputs, and the SRTYPE and DDR_CLK_EDGE parameters module ODDR ( input S, input R, @@ -14,13 +14,11 @@ parameter DDR_CLK_EDGE = "SAME_EDGE"; parameter INIT = 0; parameter SRTYPE = "SYNC"; -// verilator lint_save -// verilator lint_off MULTIDRIVEN -reg qx=INIT, hold=INIT; -// verilator lint_restore -always @(negedge C) qx <= hold; -always @(posedge C) if (CE) qx <= D1; -always @(posedge C) if (CE) hold <= D2; -assign Q = qx; +reg hold1=INIT, hold2=INIT; +always @(posedge C) if (CE) begin + hold1 <= D1; + hold2 <= D2; +end +assign Q = C ? hold1 : hold2; endmodule diff --git a/homeless/freq_demo/simplest_gray.v b/homeless/freq_demo/simplest_gray.v index 137965cee..1699089dd 100644 --- a/homeless/freq_demo/simplest_gray.v +++ b/homeless/freq_demo/simplest_gray.v @@ -1,5 +1,5 @@ // Simplest possible Gray code counter with parameterized width -// http://en.wikipedia.org/wiki/Gray_code +// https://en.wikipedia.org/wiki/Gray_code module simplest_gray #( parameter gw=4 ) ( diff --git a/localbus/Makefile b/localbus/Makefile index 844be550a..cb962f198 100644 --- a/localbus/Makefile +++ b/localbus/Makefile @@ -9,6 +9,7 @@ vpath %.v $(DSP_DIR) $(BADGER_DIR) $(BOARD_SUPPORT_DIR)/bmb7_kintex vpath %.c $(BADGER_DIR)/tests all: jit_rad_gateway_demo_badger jit_rad_gateway_demo_qf2 jit_rad_gateway_demo_cdc.txt Vjit_rad_gateway_demo jit_rad_gateway_check +all: tgen_tb jit_rad_gateway_demo_badger: $(BADGER_V) $(VERILOG) $(V_TB) -o $@ $^ @@ -35,8 +36,10 @@ live: Vjit_rad_gateway_demo ./Vjit_rad_gateway_demo +udp_port=3010 +trace # see README.md for suggestions for what to do with "make live" +tgen_tb: tgen.v dpram.v + clean: - rm -f jit_rad_gateway_demo_badger jit_rad_gateway_demo_qf2 + rm -f jit_rad_gateway_demo_badger jit_rad_gateway_demo_qf2 *_tb rm -f jit_rad_gateway_demo_cdc.txt jit_rad_gateway_demo_yosys.json rm -f Vjit_rad_gateway_demo xfer_demo.vcd jit_rad_gateway_tb *.vcd rm -rf obj_dir diff --git a/localbus/README.md b/localbus/README.md index 2c0098053..35c44a3a2 100644 --- a/localbus/README.md +++ b/localbus/README.md @@ -2,7 +2,7 @@ Many bedrock components revolve around or depend on an on-chip [localbus](https://en.wikipedia.org/wiki/Local_bus). -Speficially a stripped-down lightweight bus reminiscent +Specifically a stripped-down lightweight bus reminiscent of a VME A24 bus. In a given clock domain, expect @@ -17,10 +17,32 @@ Our use case covers real-time on-chip communication, where cycle counts are well-defined and set at configuration/synthesis time, _not_ run-time. -The key component held here is -Just In Time Readout Across Domains - [jit_rad](jit_rad.md) for short. +Components held here are +- Just In Time Readout Across Domains - [jit_rad](jit_rad.md) for short. +- localbus.vh, a helper for cycle-accurate bus simulations +- gen_regmap.py +- the much-maligned tgen, a general-purpose real-time write sequencer, + constructed as a localbus interposer; + has support in leep as assemble_tgen() and tgen_reg_sequence() + +As the jit_rad documentation explains, it's easy to shift the *write* +side of a localbus to a different clock domain. Throughput is not affected, +and the additional latency and jitter are typically irrelevant +compared to what goes on in the software and LASS host computer. +Here are the typical few lines of Verilog we use to accomplish this +clock domain shift (extracted from cmoc/cryomodule.v): +``` +wire [31:0] clk1x_data; +wire [16:0] clk1x_addr; +wire clk1x_write; +// Transfer local bus to clk1x domain +data_xdomain #(.size(32+17)) lb_to_1x( +.clk_in(lb_clk), .gate_in(lb_write), .data_in({lb_addr,lb_data}), +.clk_out(clk1x), .gate_out(clk1x_write), .data_out({clk1x_addr,clk1x_data})); +``` Also see +- [Memory gateway timing](../badger/doc/mem_gateway.svg) - [Lightweight Address Space Serialization](../badger/mem_gate.md) - Bus controller for Packet Badger [mem_gateway.v](../badger/mem_gateway.v) - Bus controller for BMB7 and QF2-pre [jxj_gate.v](../board_support/bmb7_kintex/jxj_gate.v) diff --git a/localbus/jit_rad.md b/localbus/jit_rad.md index 7c06d9fbc..4860fa82f 100644 --- a/localbus/jit_rad.md +++ b/localbus/jit_rad.md @@ -1,7 +1,7 @@ # jit_rad: Just In Time Readback Across Domains -Demo of an idea for efficiently making data from another clock domain -available to local bus readout, without violating CDC checks +A module for efficiently making data from another clock domain +available to local bus readout, without violating CDC rules The simple localbus used in bedrock and lcls2_llrf makes a lot of things easy at the Verilog source level, with minimal FPGA fabric footprint. @@ -15,13 +15,13 @@ to begin with, that's not a problem. Reading results from clock domains other than the primary is harder, because the memory gateway requires a synthesis-time choice of the exact number of -clock cycles latency. Our typical practice is to just ignore CDC problems, +clock cycles latency. One typical practice is to just ignore CDC problems, and hope that registers in domain B are not corrupted when read in domain A. -This module attempts to address that design flaw. +Converting such code to use this module will address that design flaw. We have a fair amount of warning at the beginning of a LASS UDP packet before any actual read cycles happen. Our strategy is to copy 16 words -from the app_clk domain into a 16x32 (distributed) dpram. Then those +from the app_clk domain into a 16x32 dpram. Then those data are trivially available to read out in the local bus domain. In a QF2-pre, we get about 300 ns warning, because cycles are slow (20 ns), @@ -35,7 +35,7 @@ Unlike the existing (broken CDC) case, it's not possible to repeatedly poll a signal within the same packet. Well, you can, but you're guaranteed to get the same answer each time. Unlike our slow-readout scheme, the data read is not a single-time atomic snapshot. On the plus side, -its hardware footprint is pretty small, and I claim it can be dropped in +its hardware footprint is pretty small, and it has been dropped in to lcls2_llrf without requiring any changes to high-level code. If you really want or need atomic capture, a hook is provided that lets you @@ -69,18 +69,19 @@ The other ports are - xfer_snap (supporting atomic capture use cases) - lb_error (output bit which might detect violations in the timing assumptions). -A full demo of this system is in jit_rad_gateway_demo.v. That consists -of a production local bus controller (jxj_gate or mem_gateway, preprocessor- -selectable), an instantiation of jit_rad_gateway, the external multiplexer -in the xfer_clk domain, and some minimal localbus implementation so you can -see activity. +A full demo of this system is in jit_rad_gateway_demo.v. That consists of +a production local bus controller (jxj_gate or mem_gateway, +preprocessor-selectable), an instantiation of jit_rad_gateway, +the external multiplexer in the xfer_clk domain, +and some minimal localbus implementation so you can see activity. A Verilator driver for jit_rad_gateway_demo is in xfer_sim.cpp, that can put the simulated chip on a live localhost UDP socket. A WIP iverilog test bench is in jit_rad_gateway_tb.v. -Most of the other files here (besides this one, and the Makefile) are copies -of files from scattered locations in bedrock, maybe with a few mods. +This Verilog program depends on a few files from other parts of bedrock, +as listed in the Makefile: dpram.v flag_xdomain.v reg_tech_cdc.v. +The testbench additionally depends on files from Packet Badger. ## Prerequisites and usage @@ -119,7 +120,6 @@ gtkwave xfer_demo.vcd xfer_demo.gtkw # To do: -- clean up this documentation +- expand and clean up this documentation - document acceptable relationship between lb_clk and app_clk - finish up the regression check -- test it out for real in lcls2_llrf diff --git a/localbus/jit_rad_gateway.v b/localbus/jit_rad_gateway.v index a2079ce7c..f71fdd50e 100644 --- a/localbus/jit_rad_gateway.v +++ b/localbus/jit_rad_gateway.v @@ -9,13 +9,14 @@ // and bmb7_kintex/jxj_gate have such an output. module jit_rad_gateway #( + parameter dw = 32, parameter passthrough = 1 ) ( // basic localbus hookup input lb_clk, input [3:0] lb_addr, input lb_strobe, - output [31:0] lb_odata, + output [dw-1:0] lb_odata, // control input lb_prefill, output lb_error, @@ -25,7 +26,7 @@ module jit_rad_gateway #( output xfer_snap, output xfer_strobe, output [3:0] xfer_addr, - input [31:0] xfer_odata + input [dw-1:0] xfer_odata ); generate if (passthrough) begin : thru @@ -60,7 +61,7 @@ end else begin : buffer app_running_r <= app_running; end assign xfer_addr = addr1; - dpram #(.aw(4), .dw(32)) buff( + dpram #(.aw(4), .dw(dw)) buff( .clka(app_clk), .addra(addr2), .wena(buff_we), .dina(xfer_odata), .clkb(lb_clk), .addrb(lb_addr), .doutb(lb_odata)); // diff --git a/localbus/localbus.vh b/localbus/localbus.vh index 5519ba222..edf558320 100644 --- a/localbus/localbus.vh +++ b/localbus/localbus.vh @@ -1,44 +1,48 @@ // verilog tasks for simulations -// see (Memory gateway timing)[]bedrock/badger/doc/mem_gateway.svg] +// see (Memory gateway timing)[bedrock/badger/doc/mem_gateway.svg] -// requires defining: +// a verilog program that includes this file must first make declarations like: +// localparam LB_ADW = 24; // localparam LB_READ_DELAY = 3; reg lb_write=0, lb_read=0, lb_prefill=0; -reg [17:0] lb_addr=0; -reg [31:0] lb_wdata=0; +reg [LB_ADW-1:0] lb_addr; +reg [31:0] lb_wdata; wire [31:0] lb_rdata; reg lb_rvalid=0; task lb_write_task ( - input [23:0] addr, + input [LB_ADW-1:0] addr, input [31:0] data ); begin @ (posedge lb_clk); - lb_addr = addr; - lb_wdata = data; - lb_write = 1'b1; + lb_addr <= addr; + lb_wdata <= data; + lb_write <= 1'b1; + @ (posedge lb_clk); + lb_write <= 1'b0; + lb_addr <= {LB_ADW{1'bx}}; + lb_wdata <= {32{1'bx}}; @ (posedge lb_clk); - lb_write = 1'b0; end endtask task lb_read_task ( - input [17:0] addr, + input [LB_ADW-1:0] addr, output [31:0] data ); begin @ (posedge lb_clk); - lb_addr = addr; - lb_read = 1'b1; - // repeat (4 + LB_READ_DELAY) @ (posedge lb_clk); // badger timing - repeat (0 + LB_READ_DELAY) @ (posedge lb_clk); - lb_rvalid = 1'b1; + lb_addr <= addr; + lb_read <= 1'b1; + repeat (1 + LB_READ_DELAY) @ (posedge lb_clk); + lb_rvalid <= 1'b1; + @ (posedge lb_clk); data = lb_rdata; // $display("time: %g Read ack: ADDR 0x%x DATA 0x%x", $time, addr, lb_rdata); + lb_read <= 1'b0; + lb_rvalid <= 1'b0; @ (posedge lb_clk); - lb_read = 1'b0; - lb_rvalid = 1'b0; end endtask diff --git a/cmoc/tgen.gtkw b/localbus/tgen.gtkw similarity index 100% rename from cmoc/tgen.gtkw rename to localbus/tgen.gtkw diff --git a/cmoc/tgen.v b/localbus/tgen.v similarity index 100% rename from cmoc/tgen.v rename to localbus/tgen.v diff --git a/cmoc/tgen_seq.dat b/localbus/tgen_seq.dat similarity index 100% rename from cmoc/tgen_seq.dat rename to localbus/tgen_seq.dat diff --git a/cmoc/tgen_tb.v b/localbus/tgen_tb.v similarity index 100% rename from cmoc/tgen_tb.v rename to localbus/tgen_tb.v diff --git a/peripheral_drivers/i2cbridge/assem.py b/peripheral_drivers/i2cbridge/assem.py index 8333ad80a..b59c609db 100644 --- a/peripheral_drivers/i2cbridge/assem.py +++ b/peripheral_drivers/i2cbridge/assem.py @@ -211,21 +211,24 @@ def _get_next_instruction(cls, prog_iter): try: data.append(next(prog_iter)) except StopIteration: - raise I2C_Assembler_Exception("Corrupted program detected. " + + raise I2C_Assembler_Exception( + "Corrupted program detected. " + "Program terminates before read (rd) instruction is completed") elif op_code == cls.o_wr: for n in range(n_code): try: data.append(next(prog_iter)) except StopIteration: - raise I2C_Assembler_Exception("Corrupted program detected. " + + raise I2C_Assembler_Exception( + "Corrupted program detected. " + "Program terminates before write (wr) instruction is completed") elif op_code == cls.o_wx: for n in range(n_code): try: data.append(next(prog_iter)) except StopIteration: - raise I2C_Assembler_Exception("Corrupted program detected. " + + raise I2C_Assembler_Exception( + "Corrupted program detected. " + "Program terminates before write-multi (wx) instruction is completed") # All other op_code values have no data return (op_code, n_code, data) @@ -249,7 +252,8 @@ def prnt(*args, **kwargs): while rval is not None: op_code, n_code, data = rval if loop_end: # Should not be any code after a backwards jump - raise I2C_Assembler_Exception(f"Instruction [{op_code:03b}:{n_code:05b}] " + + raise I2C_Assembler_Exception( + f"Instruction [{op_code:03b}:{n_code:05b}] " + f"at address {pc} is unreachable.") if op_code == self.o_oo: if n_code == self.n_oo_bf: # buffer_flip @@ -279,14 +283,16 @@ def prnt(*args, **kwargs): if backwards_jumped: prnt(f" After a backwards jump (srx_after_jump = {srx_after_jump})") if not srx_after_jump: - raise I2C_Assembler_Exception(f"Program address 0x{pc:03x}: Must use set_resx() after " + + raise I2C_Assembler_Exception( + f"Program address 0x{pc:03x}: Must use set_resx() after " + "a backwards jump before any read operations for consistent address of results.") pc = iprog.tell() prnt(f"pc = {pc:03x}") rval = self._get_next_instruction(iprog) if has_read and not has_buffer_flip: if not self._advanced_mode: - raise I2C_Assembler_Exception("Program has read operation but no buffer flip found." + + raise I2C_Assembler_Exception( + "Program has read operation but no buffer flip found." + " Result buffer is unreadable. Use 'advanced mode' to suppress this exception.") prnt("Program good") return @@ -306,7 +312,7 @@ def write(self, dadr, madr, data, addr_bytes=1): self._check_pc() return None - def read(self, dadr, madr, dlen, addr_bytes=1, reg_name = None): + def read(self, dadr, madr, dlen, addr_bytes=1, reg_name=None): """Add an I2C read transaction to the program. Params: int dadr : Device I2C Address @@ -482,10 +488,10 @@ def pad(self, n=None): if n is None: n = (self._pc()//32)+1 # ceil(pc/32) n = int(n) - if (n < self._pc()//32): + if n < self._pc()//32: raise I2C_Assembler_Exception(f"Cannot pad to index {n} which corresponds to an address earlier than" + " the current program counter value {self._pc()}") - elif (n > self._INDEX_MAX): + elif n > self._INDEX_MAX: raise I2C_Assembler_Exception(f"Program counter index {n} exceeds maximum {self._INDEX_MAX}") self._program += super().pad(n, self._pc()) self._check_pc() diff --git a/peripheral_drivers/i2cbridge/c2vcd.py b/peripheral_drivers/i2cbridge/c2vcd.py index dbb929515..e8f8f486d 100644 --- a/peripheral_drivers/i2cbridge/c2vcd.py +++ b/peripheral_drivers/i2cbridge/c2vcd.py @@ -4,7 +4,7 @@ def tobin(x, count=8): # Integer to binary; count is number of bits - # Props to W.J. van der Laan in http://code.activestate.com/recipes/219300/ + # Props to W.J. van der Laan in https://code.activestate.com/recipes/219300/ return list(map(lambda y: (x >> y) & 1, range(count-1, -1, -1))) diff --git a/peripheral_drivers/i2cbridge/i2c_slave_model.v b/peripheral_drivers/i2cbridge/i2c_slave_model.v index 16b6a6639..f0ce4f1b9 100644 --- a/peripheral_drivers/i2cbridge/i2c_slave_model.v +++ b/peripheral_drivers/i2cbridge/i2c_slave_model.v @@ -6,7 +6,7 @@ //// Authors: Richard Herveille (richard@asics.ws) www.asics.ws //// //// John Sheahan (jrsheahan@optushome.com.au) //// //// //// -//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// Downloaded from: https://opencores.org/projects/i2c/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// diff --git a/peripheral_drivers/idelay_scanner/scanner.tcl b/peripheral_drivers/idelay_scanner/scanner.tcl index fc0265369..c5b452b29 100644 --- a/peripheral_drivers/idelay_scanner/scanner.tcl +++ b/peripheral_drivers/idelay_scanner/scanner.tcl @@ -7,7 +7,7 @@ proc project_rpt {project_name} { # Generate implementation timing & power report report_power -file ./vivado_project/$project_name.imp_power.rpt report_timing_summary -delay_type min_max -report_unconstrained -check_timing_verbose -max_paths 10 -input_pins -file ./vivado_project/$project_name.imp_timing.rpt - # http://xillybus.com/tutorials/vivado-timing-constraints-error + # https://xillybus.com/tutorials/vivado-timing-constraints-error if {! [string match -nocase {*timing constraints are met*} [report_timing_summary -no_header -no_detailed_paths -return_string]]} { puts "Timing constraints weren't met. Please check your design." exit 2 diff --git a/peripheral_drivers/spi_mon_prog.py b/peripheral_drivers/spi_mon_prog.py index 78fd73fb2..da098455b 100644 --- a/peripheral_drivers/spi_mon_prog.py +++ b/peripheral_drivers/spi_mon_prog.py @@ -8,7 +8,7 @@ def __init__(self, IMEM=512, RMEM=128): def gen(self, cmd_arr, verbose=False): '''cmd_arr is array of tuples: (hw_sel, rnw, command[31:0])''' l_cmd = len(cmd_arr) - if (l_cmd > self.MAX_CMD): + if l_cmd > self.MAX_CMD: print("spi_mon: SPI commands (%d) exceed instruction memory size" % l_cmd) return [] imem_a = [] diff --git a/projects/cmoc_top/marblemini/cmoc_top.v b/projects/cmoc_top/marblemini/cmoc_top.v index 953ee8489..13c49ce9e 100644 --- a/projects/cmoc_top/marblemini/cmoc_top.v +++ b/projects/cmoc_top/marblemini/cmoc_top.v @@ -3,33 +3,35 @@ module cmoc_top( input GTPREFCLK_N, input SYSCLK_P, - // RGMII + // RGMII Tx port output [3:0] RGMII_TXD, output RGMII_TX_CTRL, output RGMII_TX_CLK, + + // RGMII Rx port input [3:0] RGMII_RXD, input RGMII_RX_CTRL, input RGMII_RX_CLK, // SPI boot flash programming port // BOOT_CCLK treated specially in 7-series - output BOOT_CS_B, + output BOOT_CS_B, input BOOT_MISO, - output BOOT_MOSI, - output CFG_D02, // hope R209 is DNF + output BOOT_MOSI, + output CFG_D02, // hope R209 is DNF - // One I2C bus, everything gatewayed through a TCA9548 - output TWI_SCL, + // One I2C bus, with everything gatewayed through a TCA9548 + output TWI_SCL, inout TWI_SDA, - output TWI_RST, + output TWI_RST, input TWI_INT, // SPI pins connected to microcontroller input SCLK, input CSB, input MOSI, - output MISO, - output MMC_INT, + output MISO, + output MMC_INT, // White Rabbit DAC output WR_DAC_SCLK, @@ -48,7 +50,19 @@ module cmoc_top( output VCXO_EN, - output [7:0] LED + output [7:0] LED, + + // J15 TMDS 0, 1, 2, CLK + output [3:0] TMDS_P, + output [3:0] TMDS_N, + + // Directly attached LEDs + output LD16, + output LD17, + + // Physical Pmod + // Pmod1: use LED above with Digilent mapping + input [7:0] Pmod2 ); assign VCXO_EN = 1; @@ -59,9 +73,12 @@ IBUFDS_GTE2 passi_125(.I(GTPREFCLK_P), .IB(GTPREFCLK_N), .CEB(1'b0), .O(gtpclk0) // if you don't put this BUFG in the chain to the MMCM. BUFG passg_125(.I(gtpclk0), .O(gtpclk)); +wire si570; +assign si570 = 0; + parameter in_phase_tx_clk = 1; // Standardized interface, hardware-dependent implementation -wire tx_clk, tx_clk90; +wire tx_clk, tx_clk90, clk62; wire clk_locked; wire pll_reset = 0; // or RESET? @@ -92,6 +109,7 @@ gmii_clock_handle clocks( .clk_locked(clk_locked) ); `endif +assign clk62 = 0; // Ignore dna primitive at least for now // Double-data-rate conversion wire vgmii_tx_clk, vgmii_tx_clk90, vgmii_rx_clk; @@ -121,9 +139,11 @@ gmii_to_rgmii #(.in_phase_tx_clk(in_phase_tx_clk)) gmii_to_rgmii_i( ); wire BOOT_CCLK; +wire cfg_clk; // Just for fun, so we can measure its frequency `ifndef SIMULATE -STARTUPE2 set_cclk(.USRCCLKO(BOOT_CCLK), .USRCCLKTS(1'b0)); +STARTUPE2 set_cclk(.USRCCLKO(BOOT_CCLK), .USRCCLKTS(1'b0), .CFGMCLK(cfg_clk)); `else // !`ifndef SIMULATE + assign cfg_clk = 0; assign BOOT_CCLK = tx_clk; `endif // !`ifndef SIMULATE @@ -139,6 +159,15 @@ wire [33:0] FMC1_LA_P; wire [33:0] FMC1_LA_N; wire [33:0] FMC2_LA_P; wire [33:0] FMC2_LA_N; +wire [1:0] FMC1_CK_P; +wire [1:0] FMC1_CK_N; +wire [1:0] FMC2_CK_P; +wire [1:0] FMC2_CK_N; +wire [23:0] FMC2_HA_P; +wire [23:0] FMC2_HA_N; + +// vestiges of CERN FMC tester support +wire old_scl1, old_scl2, old_sda1, old_sda2; // Real, portable implementation // Consider pulling 3-state drivers out of this @@ -147,15 +176,15 @@ marble_base #(.USE_I2CBRIDGE(1)) base( .vgmii_tx_en(vgmii_tx_en), .vgmii_tx_er(vgmii_tx_er), .vgmii_rx_clk(vgmii_rx_clk), .vgmii_rxd(vgmii_rxd), .vgmii_rx_dv(vgmii_rx_dv), .vgmii_rx_er(vgmii_rx_er), - .phy_rstn(PHY_RSTN), .clk_locked(clk_locked), + .phy_rstn(PHY_RSTN), .clk_locked(clk_locked), .si570(si570), .boot_clk(BOOT_CCLK), .boot_cs(BOOT_CS_B), .boot_mosi(BOOT_MOSI), .boot_miso(BOOT_MISO), .cfg_d02(CFG_D02), .mmc_int(MMC_INT), .ZEST_PWR_EN(ZEST_PWR_EN), - .aux_clk(SYSCLK_P), .GPS(4'b0), + .aux_clk(SYSCLK_P), .clk62(clk62), .cfg_clk(cfg_clk), .SCLK(SCLK), .CSB(CSB), .MOSI(MOSI), .MISO(MISO), .FPGA_RxD(FPGA_RxD), .FPGA_TxD(FPGA_TxD), - .twi_scl({dum_scl, FMC2_LA_P[2] , FMC1_LA_P[2], TWI_SCL}), - .twi_sda({dum_sda, FMC2_LA_N[2], FMC1_LA_N[2], TWI_SDA}), + .twi_scl({dum_scl, old_scl1, old_scl2, TWI_SCL}), + .twi_sda({dum_sda, old_sda1, old_sda2, TWI_SDA}), .TWI_RST(TWI_RST), .TWI_INT(TWI_INT), .lb_clk(lb_clk), .lb_addr(lb_addr), @@ -166,19 +195,24 @@ marble_base #(.USE_I2CBRIDGE(1)) base( .lb_data_out(lb_data_out), .lb_data_in(lb_din), .fmc_test({ - FMC2_LA_P[33:3], FMC2_LA_P[1:0], - FMC2_LA_N[33:3], FMC2_LA_N[1:0], - FMC1_LA_P[33:3], FMC1_LA_P[1:0], - FMC1_LA_N[33:3], FMC1_LA_N[1:0]}), + FMC2_HA_P, FMC2_HA_N, FMC2_CK_P, FMC2_CK_N, FMC2_LA_P, FMC2_LA_N, + FMC1_CK_P, FMC1_CK_N, FMC1_LA_P, FMC1_LA_N}), .WR_DAC_SCLK(WR_DAC_SCLK), .WR_DAC_DIN(WR_DAC_DIN), .WR_DAC1_SYNC(WR_DAC1_SYNC), .WR_DAC2_SYNC(WR_DAC2_SYNC), - .LED(LED) + .LED(LED), .GPS(Pmod2[3:0]) ); // TODO: Removing the SPI flash for now //defparam base.rtefi.p4_client.engine.seven = 1; parameter BUF_AW=13; +assign LD16 = LED[0]; +assign LD17 = LED[1]; + +// test_marble_family instantiates a "Silly little test pattern generator" for these pins +wire [3:0] tmds_q = 4'b0000; +OBUFDS obuf[0:3] (.I(tmds_q), .O(TMDS_P), .OB(TMDS_N)); + // These are meaningful LEDs, one could use them wire [2:0] D4rgb; wire [2:0] D5rgb; diff --git a/projects/cmoc_top/marblemini/oscope_rules.csv b/projects/cmoc_top/marblemini/oscope_rules.csv index 8b72b8a2c..32927357f 100644 --- a/projects/cmoc_top/marblemini/oscope_rules.csv +++ b/projects/cmoc_top/marblemini/oscope_rules.csv @@ -1,8 +1,5 @@ # Literal output follows -# This is to silence ERROR: [Place 30-574], as the pins above aren't clock dedicated -set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets i_zest_wrap/U1_lmk01801/clk_ibufgds] - create_clock -name clk1x -period 13.32 [get_nets clk_1x_90] create_clock -name clk2x -period 6.66 [get_nets clk_2x_0] diff --git a/projects/common/leep/Makefile b/projects/common/leep/Makefile new file mode 100644 index 000000000..c74922b37 --- /dev/null +++ b/projects/common/leep/Makefile @@ -0,0 +1,26 @@ +# A Makefile to run LEEP tests +THIS_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST))))) +include $(THIS_DIR)/../../../dir_list.mk + +PYTHON = python3 + +LEEP_CORE = base.py raw.py ca.py file.py logic.py + +all: test_cli test_raw + +test_cli: $(LEEP_CORE) cli.py + PYTHONPATH="$(THIS_DIR)/..:$(BUILD_DIR)" $(PYTHON) -m leep.test.test_cli test + +.PHONY: test_raw +test_raw: raw.py + @PYTHONPATH="$(THIS_DIR)/.." $(PYTHON) -m leep.test.test_raw + +# This is a test target currently only used for manual testing. Development is in progress +# for including this in automated regression tests. +server: $(LEEP_CORE) cli.py + PYTHONPATH="$(THIS_DIR)/..:$(BUILD_DIR)" $(PYTHON) -m leep.test.test_cli server + +CLEANS = test.json + +clean: + rm -rf $(CLEANS) diff --git a/projects/common/leep/base.py b/projects/common/leep/base.py index ade632836..47cfff683 100644 --- a/projects/common/leep/base.py +++ b/projects/common/leep/base.py @@ -26,12 +26,35 @@ def wrapper(*args, **kwargs): regs = args[1] if len(regs) and isinstance(regs[0], str): # it's a read operation - for reg in regs: - print('reading register {}'.format(reg)) + for n in range(len(regs)): + reg = regs[n][0] + if len(regs) > 1: + offset = regs[n][1] + try: + reg = "from address 0x{:x}".format(reg) + except ValueError: + reg = "register {}".format(reg) + if offset is not None and offset != 0: + offsetstr = " (offset {})".format(offset) + else: + offsetstr = "" + print('reading {}{}'.format(reg, offsetstr)) else: # it's a write operation, 'regs' is now a tuple, not str - for reg, val in regs: - print('writing {} to register {}'.format(val, reg)) + offset = 0 + for n in range(len(regs)): + reg, val = regs[n][:2] + if len(regs) > 2: + offset = regs[n][2] + try: + reg = "address 0x{:x}".format(reg) + except ValueError: + reg = "register {}".format(reg) + if offset is not None and offset != 0: + offsetstr = " (offset {})".format(offset) + else: + offsetstr = "" + print('writing {} to {}{}'.format(val, reg, offsetstr)) return fcn(*args, **kwargs) return wrapper diff --git a/projects/common/leep/cli.py b/projects/common/leep/cli.py index 07076b022..1a3e95fdb 100644 --- a/projects/common/leep/cli.py +++ b/projects/common/leep/cli.py @@ -7,6 +7,7 @@ import tempfile import shutil import ast +import re from collections import defaultdict @@ -17,19 +18,103 @@ _log = logging.getLogger(__name__) +def _int(s): + return int(ast.literal_eval(s)) + + +def _expandWriteVals(ss): + """Convert string 'n,m,l,k,...' into list of ints [n, m, l, k, ...] + Raise Exception if not all items separated by commas can be interpreted as integers.""" + if ',' not in ss: + return _int(ss) + vals = [_int(x) for x in ss.split(',')] + return vals + + +def parseTransaction(xact): + """ + Parse transaction string from CLI. Returns (str/int reg, int offset, int/None size, int/None write_val) + If 'reg' is type int, it is an explicit base address. + If 'size' is None, size = 1 + If 'reg' is type str, it is a register name (supposedly). + If 'size' is None, size = 2**aw (where 'aw' is the 'addr_width' from the romx JSON) + If 'write_val' is None, it's a read transaction, else it's a write of value 'write_val' + Viable formats for a transaction (read/write) in string form: + Example Implied Transaction + ------------------------------------------- + regname Read from named register (str) 'regname' + regaddr Read from explicit address (int) 'regaddr' + regname=val Write (int) 'val' to named register (str) 'regname' + regaddr=val Write (int) 'val' to explicit address (int) 'regaddr' + regname=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at the + address of named register (str) 'regname' + regaddr=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at + address (int) 'regaddr' + regname+offset Read from address = romx['regname']['base_addr'] + (int) 'offset' + regaddr+offset Read from address = (int) 'regaddr' + (int) 'offset' + regname:size Read (int) 'size' elements starting from address romx['regname']['base_addr'] + regaddr:size Read (int) 'size' elements starting from (int) 'regaddr' + regname+offset=val Write (int) 'val' to address romx['regname']['base_addr'] + (int) 'offset' + regname+offset=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at + address romx['regname']['base_addr'] + (int) 'offset' + regaddr+offset=val Write (int) 'val' to address (int) 'regaddr' + (int) 'offset' + regaddr+offset=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at + address (int) 'regaddr' + regname+offset:size Read (int) 'size' elements starting from address romx['regname']['base_addr'] + \ + (int) 'offset' + regaddr+offset:size Read (int) 'size' elements starting from (int) 'regaddr' + (int) 'offset' + I'm not sure what use case the "regaddr+offset" syntax supports, but it does no harm to include it. + NOTE! Deliberately not supporting "regname-offset" (negative offsets) as it's use case is unclear + and a '_' to '-' typo could potentially collide with legitimate transactions. + """ + restr = r"(\w+)\s*([=+:])?\s*([0-9a-fA-Fx,\-]+)?\s*([=:])?\s*([0-9a-fA-Fx,\-]+)?" + _match = re.match(restr, xact) + offset = 0 + wval = None + size = None + if _match: + groups = _match.groups() + regstr = groups[0] # Always starts with 'regname' or 'regaddr' + if groups[1] == '=': + wval = _expandWriteVals(groups[2]) + elif groups[1] == '+': + offset = _int(groups[2]) + elif groups[1] == ':': + size = _int(groups[2]) + if groups[3] == '=': + if groups[1] == '=': + raise Exception("Malformed transaction: {}".format(xact)) + wval = _expandWriteVals(groups[4]) + elif groups[3] == ':': + if size is not None: + raise Exception("Malformed transaction: {}".format(xact)) + size = _int(groups[4]) + else: + raise Exception("Failed to match: {}".format(xact)) + try: + reg = _int(regstr) + except ValueError: + reg = regstr + if size is None: + if wval is None: + size = None + else: + size = 0 + return (reg, offset, size, wval) + + def readwrite(args, dev): - for pair in args.reg: - name, _eq, val = pair.partition('=') - if len(val): - val = ast.literal_eval(val) - dev.reg_write([(name, val)]) + for xact in args.reg: + reg, offset, size, wvals = parseTransaction(xact) + if wvals is not None: + dev.reg_write_offset([(reg, wvals, offset)]) else: - value, = dev.reg_read((name,)) + value, = dev.reg_read_size(((reg, size, offset),)) try: _ = iter(value) - print("%s \t%s" % (name, ' '.join(['%x' % v for v in value]))) + print("%s \t%s" % (reg, ' '.join(['%x' % v for v in value]))) except TypeError: - print("%s \t%08x" % (name, value)) + print("%s \t%x" % (reg, value)) def listreg(args, dev): diff --git a/projects/common/leep/raw.py b/projects/common/leep/raw.py index 657a6da5f..7305d75a2 100644 --- a/projects/common/leep/raw.py +++ b/projects/common/leep/raw.py @@ -105,6 +105,23 @@ def log2(n): raise RuntimeError("yscale_rfs(%s) %s" % (wave_samp_per, e)) +def _int(s): + try: + # Catch actual ints + return int(s) + except ValueError: + pass + if hasattr(s, 'startswith'): + try: + if s.startswith('0x'): + return int(s, 16) + elif s.startswith('0b'): + return int(s, 2) + except ValueError: + pass + return None + + class LEEPDevice(DeviceBase): backend = 'leep' init_rom_addr = 0x800 @@ -132,10 +149,12 @@ def __init__(self, addr, timeout=0.1, **kws): self._readrom() - try: - app_string = self.regmap["__metadata__"]["application"] - except KeyError: - app_string = "Unknown" + app_string = "Unknown" + if self.regmap is not None: + try: + app_string = self.regmap["__metadata__"]["application"] + except KeyError: + pass self.rfs = False self.resctrl = False self.injector = False @@ -150,58 +169,75 @@ def close(self): self.sock.close() super(LEEPDevice, self).close() + def _decode(self, reg, instance=[]): + """Returns (name, addr, len, infodict)""" + info = { # Default info dict + "addr_width": 0, # TODO enable variable read size + "data_width": 32, + "sign": "unsigned", + } + _reg = _int(reg) + if _reg is not None: + return "0x{:x}".format(_reg), _reg, 1, info + if instance is not None: + reg = self.expand_regname(reg, instance=instance) + info = self.get_reg_info(reg, instance=None) + size = 2**info.get('addr_width', 0) + base_addr = info['base_addr'] + if isinstance(base_addr, (bytes, unicode)): + base_addr = int(base_addr, 0) + return reg, base_addr, size, info + @print_reg - def reg_write(self, ops, instance=[]): + def reg_write_offset(self, ops, instance=[]): assert isinstance(ops, (list, tuple)) addrs, values = [], [] - for name, value in ops: - if instance is not None: - name = self.expand_regname(name, instance=instance) - info = self.get_reg_info(name, instance=None) - L = 2**info.get('addr_width', 0) - - base_addr = info['base_addr'] - if isinstance(base_addr, (bytes, unicode)): - base_addr = int(base_addr, 0) - + for op in ops: + name, value = op[:2] + offset = 0 + if len(op) > 2: + offset = op[2] + name, base_addr, size = self._decode(name, instance)[:3] + if not hasattr(value, '__len__'): + value = [value] value = numpy.array(value).astype('I') - - if L > 1: - _log.debug('reg_write %s <- %s ...', name, value[:10]) - assert value.ndim == 1 and value.shape[0] == L, \ - ('must write whole register', value.shape, L) - # array register - for A, V in enumerate(value, base_addr): - addrs.append(A) - values.append(V) - else: - assert value.ndim == 0, 'scalar register' - _log.debug('reg_write %s <- %s', name, value) - addrs.append(base_addr) - values.append(value) + _log.debug('reg_write %s <- %s', name, value) + for A, V in enumerate(value, base_addr+offset): + addrs.append(A) + values.append(V) addrs = numpy.asarray(addrs) values = numpy.asarray(values) self.exchange(addrs, values) - @print_reg - def reg_read(self, names, instance=[]): + def reg_write(self, ops, instance=[]): + return self.reg_write_offset([(op[0], op[1], 0) for op in ops], instance=instance) + + def reg_read_size(self, name_sizes, instance=[]): + """'name_sizes' is iterable of (name, size, offset) where: + 'name' can be either a string reg name or an int address + 'size' can be an int number of elements of 'data_width' to read, or None. + 'offset' can be int positive address offset or None (alias of 0) + If 'size' is None, then (2**aw) elements will be read (where 'aw' is the + 'addr_width' of the register). + If 'name' is an address (int) then a 'size' of None implies 'size' 1.""" addrs = [] lens = [] - for name in names: - if instance is not None: - name = self.expand_regname(name, instance=instance) - info = self.get_reg_info(name, instance=None) - L = 2**info.get('addr_width', 0) - - lens.append((info, L)) - base_addr = info['base_addr'] - if isinstance(base_addr, (bytes, str, unicode)): - base_addr = int(base_addr, 0) - addrs.extend(range(base_addr, base_addr + L)) + for name_size in name_sizes: + name, size = name_size[:2] + offset = 0 + if len(name_size) > 2: + offset = name_size[2] + name, base_addr, L, info = self._decode(name, instance) + if size is None: + size = L + else: + size = int(size) + lens.append((info, size)) + addrs.extend(range(base_addr + offset, base_addr + offset + size)) raw = self.exchange(addrs) @@ -221,14 +257,21 @@ def reg_read(self, names, instance=[]): data[neg] |= mask # cast to signed data = data.astype('i4') - _log.debug('reg_read %s -> %s ...', names[i], data[:10]) + _log.debug('reg_read %s -> %s ...', name_sizes[i][0], data[:10]) # unwrap scalar from ndarray - if info.get('addr_width', 0) == 0: + # if info.get('addr_width', 0) == 0: + # TODO - does this break anything? + if L <= 1: data = data[0] ret.append(data) return ret + @print_reg + def reg_read(self, names, instance=[]): + name_sizes = [(name, None) for name in names] + return self.reg_read_size(name_sizes, instance) + def set_decimate(self, dec, instance=[]): if self.rfs: wave_shift, _Ymax = yscale_rfs(dec) @@ -477,6 +520,7 @@ def _exchange(self, addrs, values=None): msg[:2], reply[:2]) continue elif (msg[2::2] != reply[2::2]).any(): + print(f" msg[2::2] = {msg[2::2]}\n reply[2::2] = {reply[2::2]}") _log.error('reply addresses are out of order') continue @@ -498,6 +542,10 @@ def exchange(self, addrs, values=None): else: values = list(values) + if len(values) > len(addrs): + base = addrs[0] + addrs = [base+n for n in range(len(values))] + ret = numpy.zeros(len(addrs), be32) for i in range(0, len(addrs), 127): A, B = addrs[i:i + 127], values[i:i + 127] @@ -513,8 +561,8 @@ def _trysize(self, start_addr): values_preamble = numpy.array(values) self._checkrom(values, True) if self.size_rom != 0: - total_rom_size = (self.hash_descriptor_size - + self.size_desc + self.size_rom) + total_rom_size = (self.hash_descriptor_size + + self.size_desc + self.size_rom) stop_addr = end_addr + total_rom_size - self.preamble_max_size values_json = self.exchange(range(end_addr, stop_addr)) preamble_json = numpy.concatenate((values_preamble, values_json)) @@ -585,20 +633,25 @@ def _readrom(self): self.descript = None self.codehash = None self.jsonhash = None - self.regmap = None + self.regmap = None + self._has_rom = False # Try to read ROM at both addresses before raising error try: _log.debug("Trying with init_addr %d", self.init_rom_addr) self.the_rom = self._trysize(self.init_rom_addr) except (RuntimeError, ValueError, RomError): + self.the_rom = [] _log.debug("Trying with max_addr %d", self.max_rom_addr) try: self.the_rom = self._trysize(self.max_rom_addr) except RomError as e: - _log.error("raw.py: %s. Quitting." % str(e)) - raise + self.the_rom = [] + _log.error("raw.py: {}. Register name decoding disabled.".format(str(e))) except (RuntimeError, ValueError): - msg = "Could not read ROM using either start addresses" - raise ValueError(msg) - _log.debug("ROM was successfully read") + self.the_rom = [] + _log.debug("Could not read ROM using either start addresses") + if len(self.the_rom) > 0: + _log.debug("ROM was successfully read") + self._has_rom = True + return diff --git a/projects/common/leep/test/test_cli.py b/projects/common/leep/test/test_cli.py new file mode 100644 index 000000000..3c0ebadb8 --- /dev/null +++ b/projects/common/leep/test/test_cli.py @@ -0,0 +1,321 @@ + +import socket +import numpy +import json +import math +import random + +from build_rom import create_array +from ..cli import parseTransaction + + +class LASS(): + be32 = numpy.dtype('>u4') + be16 = numpy.dtype('>u2') + # see badger/mem_gate.md + OP_WRITE = 0b00 + OP_READ = 0b01 + OP_BURST = 0b10 + + _valid_ops = (OP_WRITE, OP_READ, OP_BURST) + + class InvalidPacket(Exception): + def __init__(self, msg): + super().__init__(msg) + + @classmethod + def _pack(cls, addrs, values=None, request=True, nonce=None): + """Returns (int nonce, bytes packet)""" + pad = None + if len(addrs) < 3: + pad = 3 - len(addrs) + addrs.extend([0] * pad) + values.extend([None] * pad) + msg = numpy.zeros(2 + 2 * len(addrs), dtype=cls.be32) + if nonce is None: + msg[0] = random.randint(0, 0xffffffff) + else: + msg[0] = int(nonce) & 0xffffffff + msg[1] = msg[0] ^ 0xffffffff + for i, (A, V) in enumerate(zip(addrs, values), 1): + A &= 0x00ffffff + if (request and (V is None)) or ((not request) and (V is not None)): + A |= 0x10000000 + msg[2 * i] = A + msg[2 * i + 1] = V or 0 + return (msg[0], msg.tobytes()) + + @classmethod + def _unpack(cls, pkt, request=True): + """Returns (nonce, xacts) where 'xacts' is enumerator of (addr, value) pairs. + For a request packet ('request' = True), for each (addr, value) pair, if 'value' is None, + it's a read from address 'address', otherwise it's a write to address 'addr'. + For a response packet ('request' = False), for each (addr, value) pair, if 'value' is None, + it's a response to a write to address 'address', otherwise it's the value read from address 'addr'. + """ + if len(pkt) % 4 > 0: + raise cls.InvalidPacket("Fragmented packet") + pkt_info = numpy.frombuffer(pkt, cls.be32) + nonce, xor_nonce = pkt_info[0:2] + if len(pkt_info) < 4: + raise cls.InvalidPacket("Packet too small") + if xor_nonce != (nonce ^ 0xffffffff): + raise cls.InvalidPacket("Failed nonce check") + expect_data = False + burst_count = 0 + addrs = [] + values = [] + rnw = True + addr = 0 + for word in pkt_info[2:]: + if not expect_data: + addr = word & 0xffffff + cmd = (word >> 24) + op = (cmd >> 4) & 3 + if op == cls.OP_BURST: + expect_data = False + burst_count = addr & 0x1ff # 9-bit burst count + elif op in (cls.OP_READ, cls.OP_WRITE): + expect_data = True + rnw = op == cls.OP_READ + else: + addrs.append(addr) + if request: + if rnw: + values.append(None) + else: + values.append(word) + else: + if rnw: + values.append(word) + else: + values.append(None) + if burst_count == 0: + expect_data = False + else: + addr = addr + 1 + return (nonce, addrs, values) + + @classmethod + def pack_request(cls, addrs, values=None): + return cls._pack(addrs, values, request=True) + + @classmethod + def unpack_request(cls, pkt): + """Returns (int nonce, list addrs, list values) + For each (addr, value) pair, if 'value' is None, it's a read from address 'address'. + Otherwise it's a write to address 'addr'.""" + return cls._unpack(pkt, request=True) + + @classmethod + def pack_response(cls, nonce, addrs, values=None): + return cls._pack(addrs, values, request=False, nonce=nonce) + + @classmethod + def unpack_response(cls, pkt): + """Returns (int nonce, list addrs, list values) + For each (addr, value) pair, if 'value' is None, it's a response to a write to address 'address'. + Otherwise it's the value read from address 'addr'.""" + return cls._unpack(pkt, request=False) + + +class RespondingDevice(): + @staticmethod + def mem(aw, signed=True): + if signed: + dt = numpy.int32 + else: + dt = numpy.uint32 + return numpy.zeros((1 << aw,), dtype=dt) + + def __init__(self, portnum, verbose=False): + self._verbose = verbose + self._port = int(portnum) + self.regmap = { + "foo": { + "base_addr": 0, + "addr_width": 8, + "signed": True, + "data_width": 32, + "access": "rw", + }, + "bar": { + "base_addr": 0x1000, + "addr_width": 1, + "data_width": 32, + "access": "rw", + }, + "rom": { + "base_addr": 0x4000, + "addr_width": 10, + "data_width": 16, + "access": "r", + }, + } + self._regmap = self.regmap.copy() + self.build_regmap() + + def build_regmap(self): + # Make sure the ROM gets built first + for name, entry in self.regmap.items(): + if name == "rom": + entry["mem"] = self._mkROM() + break + # Then add memory regions + for name, entry in self.regmap.items(): + if name != "rom": + entry["mem"] = self.mem(entry.get("addr_width", 0), signed=entry.get("signed", False)) + return + + def _mkROM(self): + json_file = "test.json" + with open(json_file, "w") as fd: + json.dump(self._regmap, fd) + descrip = "RespondingDevice" + arr = create_array(descrip.encode("utf-8"), json_file, placeholder_rev=True) + aw = math.ceil(math.log2(len(arr))) + mem = self.mem(aw, signed=False) + mem[:len(arr)] = arr + return mem + + def _print(self, *args, **kwargs): + if self._verbose: + print(*args, **kwargs) + + def runServer(self): + server_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) + # server_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM | socket.SOCK_NONBLOCK, 0) + server_sock.bind(('', self._port)) + print("Listening on port {}".format(self._port)) + print(" Use keyboard interrupt (ctrl+c) to terminate") + while True: + try: + msg, msg_addr = server_sock.recvfrom(1472) + nonce, addrs, values = LASS.unpack_request(msg) + for n in range(len(addrs)): + addr = addrs[n] + value = values[n] + if value is None: + # it's a read + self._print("Server Received: READ from 0x{:x}".format(addr)) + rval = self.read(addr) + values[n] = rval + else: + # it's a write + self._print("Server Received: WRITE 0x{:x} to 0x{:x}".format(value, addr)) + self.write(addr, value) + values[n] = None + nonce_out, response = LASS.pack_response(nonce, addrs, values) + server_sock.sendto(response, msg_addr) + except KeyboardInterrupt: + break + print("Done") + server_sock.close() + return + + def read(self, addr): + for name, entry in self.regmap.items(): + base = entry["base_addr"] + end = entry["base_addr"] + (1 << entry["addr_width"]) + if addr >= base and addr < end: + return entry["mem"][addr-base] + return 0 + + def write(self, addr, value): + for name, entry in self.regmap.items(): + base = entry["base_addr"] + end = entry["base_addr"] + (1 << entry["addr_width"]) + if addr >= base and addr < end: + entry["mem"][addr-base] = value + return None + return None + + +def test_parseTransaction(): + # Baddies raise Exception, so we'll indicate this as the expected + # result by setting the "result" value below to None + dd = { + # CLI string: result (reg, offset, read_size, write_vals) + # regname Read from named register (str) 'regname' + "foo": ("foo", 0, None, None), + # regaddr Read from explicit address (int) 'regaddr' + "0x100": (0x100, 0, None, None), + # regname=val Write (int) 'val' to named register (str) 'regname' + "foo=42": ("foo", 0, 0, 42), + "bar=0x42": ("bar", 0, 0, 0x42), + "foo_baz=0b100": ("foo_baz", 0, 0, 0b100), + # regaddr=val Write (int) 'val' to explicit address (int) 'regaddr' + "0x123=100": (0x123, 0, 0, 100), + "123=0xabc": (123, 0, 0, 0xabc), + "0b1010=-10": (0b1010, 0, 0, -10), + # regname=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at the + # address of named register (str) 'regname' + "reg_foo=1,2,3,4,5": ("reg_foo", 0, 0, [1, 2, 3, 4, 5]), + # regaddr=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at + # address (int) 'regaddr' + "0x4000=1,-1,0,42,0x10": (0x4000, 0, 0, [1, -1, 0, 42, 0x10]), + # regname+offset Read from address = romx['regname']['base_addr'] + (int) 'offset' + "BINGO+100": ("BINGO", 100, None, None), + # regaddr+offset Read from address = (int) 'regaddr' + (int) 'offset' + "0x100+100": (0x100, 100, None, None), + # regname:size Read (int) 'size' elements starting from address romx['regname']['base_addr'] + "_reg_:32": ("_reg_", 0, 32, None), + # regaddr:size Read (int) 'size' elements starting from (int) 'regaddr' + "0:0xff": (0, 0, 0xff, None), + # regname+offset=val Write (int) 'val' to address romx['regname']['base_addr'] + (int) 'offset' + "bandit+0x100=5000": ("bandit", 0x100, 0, 5000), + # regname+offset=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at + # address romx['regname']['base_addr'] + (int) 'offset' + "status+0x20=50,40,0x30": ("status", 0x20, 0, [50, 40, 0x30]), + # regaddr+offset=val Write (int) 'val' to address (int) 'regaddr' + (int) 'offset' + "128+0xc0=-1000": (128, 0xc0, 0, -1000), + # regaddr+offset=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at + # address (int) 'regaddr' + "0x128+0xc0=1,0,1,0,2": (0x128, 0xc0, 0, [1, 0, 1, 0, 2]), + # regname+offset:size Read (int) 'size' elements starting from address romx['regname']['base_addr'] + \ + # (int) 'offset' + "Socks+0x100:100": ("Socks", 0x100, 100, None), + # regaddr+offset:size Read (int) 'size' elements starting from (int) 'regaddr' + (int) 'offset' + "0x4000+15:0b1111": (0x4000, 15, 0b1111, None), + } + errors = 0 + for _input, _expected in dd.items(): + try: + result = parseTransaction(_input) + except Exception: + result = None + if result != _expected: + print("Failed on input: {}.\n Expected: {}\n Result: {}".format(_input, _expected, result)) + errors += 1 + return errors + + +def doTests(args): + errors = 0 + errors += test_parseTransaction() + if errors == 0: + print("PASSED") + return 0 + else: + print("FAILED with {} errors".format(errors)) + return 1 + + +def runServer(args): + dev = RespondingDevice(4592, verbose=False) + dev.runServer() + return 0 + + +if __name__ == "__main__": + import argparse + import sys + parser = argparse.ArgumentParser("LEEP CLI Test") + parser.set_defaults(handler=lambda args: None) + subparsers = parser.add_subparsers(help="Subcommands") + parserServer = subparsers.add_parser("server", help="Run a simulated LEEPDevice server.") + parserServer.set_defaults(handler=runServer) + parserTest = subparsers.add_parser("test", help="Run regression tests.") + parserTest.set_defaults(handler=doTests) + args = parser.parse_args() + sys.exit(args.handler(args)) diff --git a/projects/common/leep/test/test_raw.py b/projects/common/leep/test/test_raw.py index 21b2f329d..6c15f8df8 100644 --- a/projects/common/leep/test/test_raw.py +++ b/projects/common/leep/test/test_raw.py @@ -189,3 +189,29 @@ def test_array(self): self.assertEqual(self.serv.data[101], 0xdeadbeef) self.assertEqual(self.serv.data[102], 0x12345679) self.assertEqual(self.serv.data[103], 0xdeadbeef) + + +def test_raw_int(): + from leep.raw import _int + tests = { + "foo": None, + "123": 123, + "0x100": 0x100, + "0b1000": 0b1000, + "0xreg": None, + "0bentry": None, + } + for key, val in tests.items(): + if val != _int(key): + raise Exception("Test failed _int({}) = {} != {}".format(key, _int(key), val)) + return True + + +def doTests(): + test_raw_int() + print("PASS") + return + + +if __name__ == "__main__": + doTests() diff --git a/projects/comms_top/gige_eth/Makefile b/projects/comms_top/gige_eth/Makefile index 88d8fa574..f49bab27c 100644 --- a/projects/comms_top/gige_eth/Makefile +++ b/projects/comms_top/gige_eth/Makefile @@ -22,10 +22,10 @@ all: gen $(APP_NAME).bit $(APP_NAME).bit: $(IP_TCL) hwload_ac701: - xc3sprog -c jtaghs1_fast $(BITFILE) + xc3sprog -c jtaghs1_fast $(SERIAL_NUM_OPT) $(BITFILE) hwtest_ac701: - ping -c2 $(SFP_IP) && cd ../test && python3 comms_top_test.py -t $(SFP_IP) -cf mem_test.cf + ping -c 2 $(SFP_IP) && cd ../test && $(PYTHON) comms_top_test.py -t $(SFP_IP) -cf mem_test.cf ifneq (,$(findstring bit,$(MAKECMDGOALS))) ifneq (,$(findstring bits,$(MAKECMDGOALS))) diff --git a/projects/comms_top/test/comms_top_test.py b/projects/comms_top/test/comms_top_test.py index 92303a387..b73c137da 100755 --- a/projects/comms_top/test/comms_top_test.py +++ b/projects/comms_top/test/comms_top_test.py @@ -117,13 +117,13 @@ def _badarg(self, cmd): print("ERROR: Bad arguments to command {}".format(cmd)) def _pprint(self, cmd, args): - if (len(args) == 0): + if len(args) == 0: self._badarg(cmd) return print("\n> {}\n".format(" ".join(args))) def _pread(self, cmd, args): - if (len(args) != 1): + if len(args) != 1: self._badarg(cmd) return raddr = int(args[0].lstrip(':'), 0) @@ -133,7 +133,7 @@ def _pread(self, cmd, args): print("{} {} RDATA = {} ({})".format(cmd, " ".join(args), d_hex, d_asc)) def _pwrite(self, cmd, args): - if (len(args) != 2): + if len(args) != 2: self._badarg(cmd) return waddr = int(args[0].lstrip(':'), 0) @@ -144,7 +144,7 @@ def _pwrite(self, cmd, args): print("{} {}".format(cmd, " ".join(args))) def _prdmem(self, cmd, args): - if (len(args) != 3): + if len(args) != 3: self._badarg(cmd) return base_addr = int(args[0].lstrip(':'), 0) @@ -162,7 +162,7 @@ def _prdmem(self, cmd, args): print("{} {} : Successfully read {} memory positions".format(cmd, " ".join(args), read_length)) def _pwait(self, cmd, args): - if (len(args) != 1): + if len(args) != 1: self._badarg(cmd) return tsleep = int(args[0]) @@ -171,7 +171,7 @@ def _pwait(self, cmd, args): time.sleep(tsleep) def _pcmp(self, cmd, args): - if (len(args) != 2): + if len(args) != 2: self._badarg(cmd) return diff --git a/projects/oscope/bmb7_cu/Makefile b/projects/oscope/bmb7_cu/Makefile index afd967198..28c2669f3 100644 --- a/projects/oscope/bmb7_cu/Makefile +++ b/projects/oscope/bmb7_cu/Makefile @@ -57,7 +57,7 @@ Voscope_top_leep: Voscope_top_tb $(APP_NAME)_regmap.json @echo "----- Oscope leep list self-test -----" timeout 20 ./$< & PYTHONPATH=$(PROJECTS_DIR)/common/ $(PYTHON) -m leep.cli leep://localhost:3010 list > leep_list.txt - awk -F'"' '/ {/{print $$2}' $(APP_NAME)_regmap.json | cmp leep_list.txt - + $(AWK) -F'"' '/ {/{print $$2}' $(APP_NAME)_regmap.json | cmp leep_list.txt - Voscope_top_live: Voscope_top_tb $(APP_NAME)_regmap.json @echo "----- Oscope build rom live test -----" diff --git a/projects/oscope/common/application_top.sv b/projects/oscope/common/application_top.sv index 444dd136d..b98d7ac26 100644 --- a/projects/oscope/common/application_top.sv +++ b/projects/oscope/common/application_top.sv @@ -55,11 +55,11 @@ module application_top( // Needs placing before usage of any top-level registers wire clk1x_clk, clk2x_clk, lb4_clk; -(* external, signal_type="single-cycle" *) reg [0:0] lamp_test_trig = 0; // top-level single-cycle -(* external, signal_type="single-cycle" *) reg [0:0] ctrace_start = 0; // top-level single-cycle +// Note: the syntax for these top-level registers is not quite compatible with "old" newad (* external *) reg [31:0] icc_cfg = 0; // top-level -(* external *) reg [7:0] tag_now = 0; // top-level (* external *) reg [1:0] domain_jump_realign = 0; // top-level +// newad-force clk1x domain +(* external, cd="clk1x" *) reg [7:0] tag_now = 0; // top-level `AUTOMATIC_decode @@ -157,10 +157,8 @@ wire [27:0] frequency_adc; wire [27:0] frequency_4xout; wire [27:0] frequency_clkout3; wire [27:0] frequency_dac_dco; -wire [27:0] frequency_gtx_tx; -wire [27:0] frequency_gtx_rx; -wire [15:0] hist_dout; -wire [1:0] hist_status; +wire [15:0] hist_dout=0; +wire [1:0] hist_status=0; wire [15:0] phasex_dout; wire phasex_ready; wire phasex_present; @@ -173,12 +171,14 @@ wire [7:0] llspi_result; wire wave1_available, wave0_available; wire [7:0] rawadc_avail; wire [31:0] banyan_data; -wire [31:0] banyan_status; -wire [31:0] trace_data; -wire [31:0] trace_status1; -wire [31:0] trace_status2; -wire [7:0] slow_data; -wire [0:0] slow_data_ready; +// banyan status register was constructed without regard to clock domains - please revisit +wire [31:0] banyan_status_x; +reg [31:0] banyan_status; always @(posedge lb_clk) banyan_status <= banyan_status_x; +wire [31:0] trace_data=0; +wire [31:0] trace_status1=0; +wire [31:0] trace_status2=0; +wire [7:0] slow_data=0; +wire [0:0] slow_data_ready=0; wire [31:0] phase_status_U2; wire [31:0] phase_status_U3; reg [15:0] crc_errors=0; @@ -187,31 +187,8 @@ wire [7:0] scanner_result_val; wire [7:0] slow_chain_out; wire [19:0] icc_data_U50, icc_data_U32; wire [8:0] qsfp_result; -wire [31:0] ctrace_out; -wire [0:0] ctrace_running; -parameter ctrace_aw=12; -wire [ctrace_aw-1:0] ctrace_pc; -wire [ctrace_aw-0:0] ctrace_status = {ctrace_pc, ctrace_running}; -wire [31:0] ccr1_summary; -wire [31:0] ccr1_counts; -wire [31:0] ccr1_cavity0_status; -wire [31:0] ccr1_cavity1_status; -wire [31:0] ccr1_rev_id; -reg [15:0] cc1_latency=0; -wire [15:0] cct1_local_frame_counter; // output of chitchat_tx -wire signed [17:0] cavity0_detune; -wire signed [17:0] cavity1_detune; -reg [0:0] cct1_cavity0_detune_valid=0; -reg [0:0] cct1_cavity1_detune_valid=0; -wire cavity0_detune_stb; -wire cavity1_detune_stb; -wire [6:0] cavity0_sat_count; -wire [6:0] cavity1_sat_count; -wire [2:0] dtrace_status; -wire [21:0] telem_monitor; wire signed [13:0] fdbk_drive_lb_out; wire [25:0] freq_multi_count_out; -wire [1:0] dac_enabled; wire [31:0] U15_spi_addr_rdbk = {zif_cfg.U15_sdo_addr, zif_cfg.U15_spi_rdbk}; wire [1:0] U15_spi_status = {zif_cfg.U15_spi_ready, zif_cfg.U15_sdio_as_sdo}; wire [31:0] U18_spi_addr_rdbk = {zif_cfg.U18_sdo_addr, zif_cfg.U18_spi_rdbk}; @@ -231,15 +208,25 @@ wire [31:0] hello_1 = "o wo"; wire [31:0] hello_2 = "rld!"; wire [31:0] hello_3 = 32'h0d0a0d0a; wire [31:0] ffffffff = 32'hffffffff; -wire [31:0] U2dout_lsb = zif_cfg.U2_dout[31:0]; -wire [31:0] U2dout_msb = zif_cfg.U2_dout[63:32]; -wire [31:0] U3dout_lsb = zif_cfg.U3_dout[31:0]; -wire [31:0] U3dout_msb = zif_cfg.U3_dout[63:32]; wire [19:0] idelay_value_out_U2_lsb = zif_cfg.U2_idelay_value_out[19:0]; wire [19:0] idelay_value_out_U2_msb = zif_cfg.U2_idelay_value_out[39:20]; wire [19:0] idelay_value_out_U3_lsb = zif_cfg.U3_idelay_value_out[19:0]; wire [19:0] idelay_value_out_U3_msb = zif_cfg.U3_idelay_value_out[39:20]; +// Only useful if the ADC data is static, which is the plan when +// configured to emit a test pattern during initial setup. +// Maybe just get rid of this, and use the real data capture in banyan memory. +reg [31:0] U2dout_lsb = 0; +reg [31:0] U2dout_msb = 0; +reg [31:0] U3dout_lsb = 0; +reg [31:0] U3dout_msb = 0; +always @(posedge lb_clk) begin + U2dout_lsb <= zif_cfg.U2_dout[31:0]; + U2dout_msb <= zif_cfg.U2_dout[63:32]; + U3dout_lsb <= zif_cfg.U3_dout[31:0]; + U3dout_msb <= zif_cfg.U3_dout[63:32]; +end + // Very basic pipelining of read process reg [23:0] lb_addr_r=0; always @ (posedge lb_clk) begin @@ -281,10 +268,6 @@ always @(posedge lb_clk) begin 4'h1: reg_bank_1 <= U3dout_msb; 4'h2: reg_bank_1 <= idelay_value_out_U3_lsb; 4'h3: reg_bank_1 <= idelay_value_out_U3_msb; - 4'ha: reg_bank_1 <= dtrace_status; - 4'hc: reg_bank_1 <= ctrace_status; // alias: ctrace_running - 4'hd: reg_bank_1 <= frequency_gtx_tx; - 4'he: reg_bank_1 <= frequency_gtx_rx; 4'hf: reg_bank_1 <= hist_status; default: reg_bank_1 <= 32'hfaceface; endcase @@ -310,16 +293,9 @@ always @(posedge lb_clk) begin case (lb_addr[3:0]) 4'h0: reg_bank_3 <= phase_status_U3; // alias: clk_phase_diff_out_U3 4'h1: reg_bank_3 <= crc_errors; - 4'h2: reg_bank_3 <= cavity0_detune; - 4'h3: reg_bank_3 <= cavity1_detune; - 4'h4: reg_bank_3 <= cavity0_sat_count; - 4'h5: reg_bank_3 <= cavity1_sat_count; - 4'h6: reg_bank_3 <= dac_enabled; // xxxx37 unused 4'h8: reg_bank_3 <= U15_spi_addr_rdbk; // alias: U15_spi_rdbk 4'h9: reg_bank_3 <= U15_spi_status; - 4'ha: reg_bank_3 <= cct1_cavity0_detune_valid; - 4'hb: reg_bank_3 <= cct1_cavity1_detune_valid; 4'hc: reg_bank_3 <= U18_spi_addr_rdbk; // alias: U18_spi_rdbk 4'hd: reg_bank_3 <= U18_spi_status; // xxxx3e unused @@ -332,18 +308,10 @@ always @(posedge lb_clk) begin 4'h4: reg_bank_4 <= trace_status1; 4'h5: reg_bank_4 <= trace_status2; 4'h7: reg_bank_4 <= slow_data_ready; - 4'h8: reg_bank_4 <= telem_monitor; default: reg_bank_4 <= 32'hfaceface; endcase case (lb_addr[3:0]) - // Most of these are in the wrong clock domain - 4'h0: reg_bank_5 <= ccr1_summary; - 4'h1: reg_bank_5 <= ccr1_counts; - 4'h2: reg_bank_5 <= ccr1_cavity0_status; - 4'h3: reg_bank_5 <= ccr1_cavity1_status; - 4'h4: reg_bank_5 <= ccr1_rev_id; - 4'h5: reg_bank_5 <= cc1_latency; - 4'h6: reg_bank_5 <= cct1_local_frame_counter; + 4'h1: reg_bank_5 <= 1; default: reg_bank_5 <= 32'hfaceface; endcase // All of the following rhs have had one stage of decode pipeline; @@ -370,8 +338,8 @@ always @(posedge lb_clk) begin end wire rawadc_trig_x; -(* lb_automatic *) -digitizer_config digitizer_config // auto +(* lb_automatic, cd="clk1x" *) +digitizer_config digitizer_config // auto clk1x ( .lb_clk(lb_clk), .lb_strobe(lb_strobe), @@ -383,7 +351,7 @@ digitizer_config digitizer_config // auto .rawadc_trig_x(rawadc_trig_x), .adc_clk(adc_clk), .adc_data(adc_data), - .banyan_status(banyan_status), + .banyan_status(banyan_status_x), .phasex_dout(phasex_dout), .phase_status_U2(phase_status_U2), .phase_status_U3(phase_status_U3), diff --git a/projects/oscope/common/digitizer_config.sv b/projects/oscope/common/digitizer_config.sv index 1a64aa964..c2e6153ee 100644 --- a/projects/oscope/common/digitizer_config.sv +++ b/projects/oscope/common/digitizer_config.sv @@ -45,22 +45,15 @@ module digitizer_config( output [1:0] clk_status, // software-settable + // adc_clk domain (* external *) input [31:0] periph_config, // external (* external *) input [15:0] bitslip, // external (* external *) - input [1:0] U15_spi_read_and_start_r, // external - //input U15_spi_read_r, // external - (* external *) input [31:0] U15_spi_data_addr_r, // external - //input [15:0] U15_spi_addr_r, // external - (* external *) - input [1:0] U18_spi_read_and_start_r, // external - //input U18_spi_read_r, // external (* external *) input [31:0] U18_spi_data_addr_r, // external - //input [7:0] U18_spi_addr_r, // external (* external *) input U2_clk_reset_r, // external (* external *) @@ -77,29 +70,36 @@ module digitizer_config( input mmcm_reset_r, // external (* external *) input idelayctrl_reset_r, // external + // lb_clk domain - (* external *) + // newad-force lb domain + (* external, cd="lb" *) + input [1:0] U15_spi_read_and_start_r, // external + (* external, cd="lb" *) + input [1:0] U18_spi_read_and_start_r, // external + (* external, cd="lb" *) input [7:0] banyan_mask, // external - (* external, signal_type="single-cycle" *) + (* external, signal_type="single-cycle", cd="lb" *) input phasex_trig, // external single-cycle - (* external, signal_type="we-strobe" *) + (* external, signal_type="we-strobe", cd="lb" *) input llspi_we, // external we-strobe input llspi_re, // -- external strobe - (* external, signal_type="we-strobe" *) + (* external, signal_type="we-strobe", cd="lb" *) input clk_status_we, // external we-strobe - (* external *) + (* external, cd="lb" *) input [4:0] scanner_debug, // external input autoset_enable, // -- external input scan_trigger, // -- external single-cycle - (* external, signal_type="we-strobe" *) + (* external, signal_type="we-strobe", cd="lb" *) input scan_trigger_we, // external we-strobe // lb_clk domain, but only because I flag_xdomain to adc_clk - (* external, signal_type="single-cycle" *) + (* external, signal_type="single-cycle", cd="lb" *) input rawadc_trig, // external single-cycle - (* external *) - input [9:0] adc_downsample_ratio, // external // adc_clk domain + // newad-force clk1x domain + (* external *) + input [9:0] adc_downsample_ratio, // external (* external *) input [9:0] sync_ad7794_cset, // external (* external *) @@ -219,6 +219,12 @@ assign clk_status = clk_status_r; flag_xdomain rawadc_trig_xdomain (.clk1(lb_clk), .flagin_clk1(rawadc_trig), .clk2(adc_clk), .flagout_clk2(rawadc_trig_x)); +// One more clock-domain change, so idelay_scanner can run in pure lb_clk domain +wire [127:0] permuted_data; // from banyan_mem +wire [15:0] scanner_adc_val = permuted_data[15:0]; +reg [15:0] scanner_adc_val_r=0; +always @(posedge lb_clk) scanner_adc_val_r <= scanner_adc_val; + // 16 idelay registers mapped to lb_addr 112-127 // See idelay_base in static_oscope_regmap.json wire scan_running; @@ -227,8 +233,6 @@ wire [4:0] hw_data; wire hw_strobe; wire [7:0] scanner_banyan_mask; wire [2:0] scanner_adc_num; // not used -wire [127:0] permuted_data; // from banyan_mem -wire [15:0] scanner_adc_val = permuted_data[15:0]; wire lb_idelay_write = lb_strobe & ~lb_rd & (lb_addr[23:4] == 20'h19007); idelay_scanner #(.use_decider(1)) scanner( .lb_clk(lb_clk), .lb_addr(lb_addr[3:0]), .lb_data(lb_dout[4:0]), @@ -240,7 +244,7 @@ idelay_scanner #(.use_decider(1)) scanner( .debug_sel(scanner_debug[4]), .debug_addr(scanner_debug[3:0]), .hw_addr(hw_addr), .hw_data(hw_data), .hw_strobe(hw_strobe), .banyan_mask(scanner_banyan_mask), .adc_num(scanner_adc_num), - .adc_clk(adc_clk), .adc_val(scanner_adc_val) + .adc_clk(lb_clk), .adc_val(scanner_adc_val_r) ); // process the output hw_ bus from idelay_scanner, sending to IDELAYE2 @@ -315,20 +319,16 @@ assign zif_cfg.U2_dco_clk_in = zif_cfg.U2_dco_clk_out; `define CONFIG_PHASE_DIFF `ifdef CONFIG_PHASE_DIFF +wire err_ff_U2, err_ff_U3; +wire [12:0] phdiff_out_U2, phdiff_out_U3; +wire [13:0] vfreq_out_U2, vfreq_out_U3; // Measure the phases of the two BUFR outputs relative to adc_clk (U2's BUFR after MMCM and BUFG) -wire [12:0] clk_phase_diff_out_U2, clk_phase_diff_out_U3; -wire [13:0] clk_phase_diff_freq_U2, clk_phase_diff_freq_U3; -wire clk_phase_diff_locked_U2, clk_phase_diff_locked_U3; -phase_diff phase_diff_U2(.uclk1(zif_cfg.U2_clk_div_bufg), .uclk2(zif_cfg.U2_clk_div_bufr), .sclk(clk200), - .rclk(lb_clk), .phdiff_out(clk_phase_diff_out_U2), - .ext_div1(1'b0), .ext_div2(1'b0), - .vfreq_out(clk_phase_diff_freq_U2), .locked(clk_phase_diff_locked_U2)); -assign phase_status_U2 = {~clk_phase_diff_locked_U2, clk_phase_diff_freq_U2,4'b0, clk_phase_diff_out_U2}; -phase_diff phase_diff_U3(.uclk1(zif_cfg.U2_clk_div_bufg), .uclk2(zif_cfg.U3_clk_div_bufr), .sclk(clk200), - .rclk(lb_clk), .phdiff_out(clk_phase_diff_out_U3), - .ext_div1(1'b0), .ext_div2(1'b0), - .vfreq_out(clk_phase_diff_freq_U3), .locked(clk_phase_diff_locked_U3)); -assign phase_status_U3 = {~clk_phase_diff_locked_U3,clk_phase_diff_freq_U3,4'b0,clk_phase_diff_out_U3}; +phase_diff #(.delta(33)) phase_diff_U2(.uclk1(zif_cfg.U2_clk_div_bufg), .uclk2(zif_cfg.U2_clk_div_bufr), .uclk2g(1'b1), .sclk(clk200), + .rclk(lb_clk), .phdiff_out(phdiff_out_U2), .vfreq_out(vfreq_out_U2), .err_ff(err_ff_U2)); +phase_diff #(.delta(33)) phase_diff_U3(.uclk1(zif_cfg.U2_clk_div_bufg), .uclk2(zif_cfg.U3_clk_div_bufr), .uclk2g(1'b1), .sclk(clk200), + .rclk(lb_clk), .phdiff_out(phdiff_out_U3), .vfreq_out(vfreq_out_U3), .err_ff(err_ff_U3)); +assign phase_status_U2 = {err_ff_U2, vfreq_out_U2, 4'b0, phdiff_out_U2}; +assign phase_status_U3 = {err_ff_U3, vfreq_out_U3, 4'b0, phdiff_out_U3}; `else assign phase_status_U2 = 0; assign phase_status_U3 = 0; @@ -336,7 +336,9 @@ assign phase_status_U3 = 0; `define CONFIG_BANYAN `ifdef CONFIG_BANYAN -// Banyan-routed memory, simple one-shot fill for now +// Banyan-routed memory, that features a simple one-shot fill. +// Use rawadc_trig for a direct asynchronous fill, or rawadc_trig_req +// to start filling at the next external trigger (ext_trig). parameter banyan_aw = 14; // 8 blocks of RAM, each 16K x 16 reg banyan_run=0, banyan_run_d=0; wire rollover, full; diff --git a/projects/oscope/common/digitizer_slowread.v b/projects/oscope/common/digitizer_slowread.v index 8bc090ac4..ad02437e5 100644 --- a/projects/oscope/common/digitizer_slowread.v +++ b/projects/oscope/common/digitizer_slowread.v @@ -67,15 +67,14 @@ timestamp ts(.clk(adc_clk), .aux_trig(1'b0), .slow_op(slow_op), .slow_snap(slow_ // the buffer, tag_old shows it at the begin-time of the buffer. Not perfect // because of non-boxcar filtering and sloppy pipelining. reg [7:0] tag_old=0; -`define SLOW_SR_LEN 17*16 -`define SLOW_SR_DATA { \ - U3DA_min, U3DA_max, U3DB_min, U3DB_max, U3DC_min, U3DC_max, U3DD_min, U3DD_max, \ - U2DA_min, U2DA_max, U2DB_min, U2DB_max, U2DC_min, U2DC_max, U2DD_min, U2DD_max, \ - tag_now, tag_old } -parameter sr_length = `SLOW_SR_LEN; +parameter sr_length = 17*16; +wire [sr_length-1:0] slow_sr_data = { + U3DA_min, U3DA_max, U3DB_min, U3DB_max, U3DC_min, U3DC_max, U3DD_min, U3DD_max, + U2DA_min, U2DA_max, U2DB_min, U2DB_max, U2DC_min, U2DC_max, U2DD_min, U2DD_max, + tag_now, tag_old }; reg [sr_length-1:0] slow_read=0; always @(posedge adc_clk) if (slow_op) begin - slow_read <= slow_snap ? `SLOW_SR_DATA : {slow_read[sr_length-9:0],timestamp_out}; + slow_read <= slow_snap ? slow_sr_data : {slow_read[sr_length-9:0],timestamp_out}; if (slow_snap) tag_old <= tag_now; end diff --git a/projects/oscope/common/llspi.mk b/projects/oscope/common/llspi.mk index 5ebb1378d..0515843ae 100644 --- a/projects/oscope/common/llspi.mk +++ b/projects/oscope/common/llspi.mk @@ -23,7 +23,7 @@ PYTHON = python3 $(VVP) $< $(VFLAGS) %.dat: %_tb - vvp $< > $@ + $(VVP) $< > $@ all: llspi_tb diff --git a/projects/oscope/common/llspi.v b/projects/oscope/common/llspi.v index a5793c0d4..b9636c8b5 100644 --- a/projects/oscope/common/llspi.v +++ b/projects/oscope/common/llspi.v @@ -7,7 +7,7 @@ module llspi #( parameter dbg = "false", parameter pace_set = 6, // Can override to 2 or 3 for testing - parameter infifo_aw=5 + parameter infifo_aw = 5 ) ( input clk, // timespec 5.3 ns // Physical FMC pins connected to digitizer board @@ -139,7 +139,7 @@ spi_eater eater(.clk(clk), .pace(pace), .empty(empty), // Read results get pushed into this FIFO wire [7:0] result_unlatched; -wire [3:0] result_count; +wire [4:0] result_count; shortfifo #(.dw(8), .aw(4)) output_fifo(.clk(clk), .we(result_we), .din(result), .re(result_re), .dout(result_unlatched), @@ -148,6 +148,8 @@ shortfifo #(.dw(8), .aw(4)) output_fifo(.clk(clk), always @(posedge clk) if (result_re) host_result <= result_unlatched; // Status register available for host polling -assign status = {3'b0, empty, result_count}; +// If you push 16 results into shortfifo, don't expect to get +// a useful result from this status register! +assign status = {3'b0, empty, result_count[3:0]}; endmodule diff --git a/projects/oscope/marble_family/Makefile b/projects/oscope/marble_family/Makefile index c0334cb4a..4ec3dab28 100644 --- a/projects/oscope/marble_family/Makefile +++ b/projects/oscope/marble_family/Makefile @@ -6,7 +6,8 @@ XILINX_TOOL := VIVADO include ../../../dir_list.mk -OSCOPE_COMMON_DIR=../common +OSCOPE_COMMON_DIR = ../common +MARBLE_FAMILY_DIR = ../../test_marble_family APP_NAME = oscope @@ -16,7 +17,7 @@ include $(BUILD_DIR)/newad_top_rules.mk include $(OSCOPE_COMMON_DIR)/rules.mk -VFLAGS_DEP += -y$(DSP_DIR) -y$(BOARD_SUPPORT_DIR)/marblemini -y$(BOARD_SUPPORT_DIR)/zest -y$(FPGA_FAMILY_DIR) -y$(FPGA_FAMILY_DIR)/xilinx -y$(FPGA_FAMILY_DIR)/iserdes -y$(ISERDES_DIR) -y$(XILINX_DIR) -y$(SERIAL_IO_DIR) -y. -y$(PERIPH_DRIVERS_DIR) -y$(PERIPH_DRIVERS_DIR)/idelay_scanner -y$(OSCOPE_COMMON_DIR) -y$(HOMELESS_DIR) -y$(BADGER_DIR) -y$(BADGER_DIR)/tests -y$(BADGER_DIR)/tests/kc705 -y../../test_marble_family/ -y../../test_marble_family/pps_lock -y$(PERIPH_DRIVERS_DIR)/i2cbridge -y$(HOMELESS_DIR)/freq_demo -DSIMULATE +VFLAGS_DEP += -y$(DSP_DIR) -y$(BOARD_SUPPORT_DIR)/marblemini -y$(BOARD_SUPPORT_DIR)/zest -y$(FPGA_FAMILY_DIR) -y$(FPGA_FAMILY_DIR)/xilinx -y$(FPGA_FAMILY_DIR)/iserdes -y$(SERIAL_IO_DIR) -y. -y$(PERIPH_DRIVERS_DIR) -y$(PERIPH_DRIVERS_DIR)/idelay_scanner -y$(OSCOPE_COMMON_DIR) -y$(HOMELESS_DIR) -y$(BADGER_DIR) -y$(BADGER_DIR)/tests -y$(BADGER_DIR)/tests/kc705 -y$(MARBLE_FAMILY_DIR) -y$(MARBLE_FAMILY_DIR)/pps_lock -y$(PERIPH_DRIVERS_DIR)/i2cbridge -y$(HOMELESS_DIR)/freq_demo -DSIMULATE LB_AW = 23 NEWAD_DIRS += $(OSCOPE_COMMON_DIR) @@ -26,7 +27,7 @@ RTEFI_EXTRA_V = spi_flash_engine.v include $(BADGER_DIR)/rules.mk $(AUTOGEN_DIR)/moving_average.v: $(DSP_DIR)/moving_average/moving_average.py - $(PYTHON) $(DSP_DIR)/moving_average/moving_average.py > $(AUTOGEN_DIR)/moving_average.v + $(PYTHON) $(DSP_DIR)/moving_average/moving_average.py | grep -v '(* top' > $@ oscope_features: $(BUILD_DIR)/gen_features.py oscope_features.yaml $(PYTHON) $< -i $(filter %.yaml, $^) -c marble_v2 --split @@ -47,10 +48,44 @@ zest_connector.csv: remap_gen.py system_top.xdc: $(BOARD_SUPPORT_DIR)/$(HARDWARE)/Marble.xdc $(BOARD_SUPPORT_DIR)/$(HARDWARE)/pin_map.csv zest_connector.csv oscope_rules.csv $(PYTHON) $(BADGER_DIR)/tests/meta-xdc.py $^ > $@ -oscope_top.sv: $(AUTOGEN_DIR)/config_romx.v $(RTEFI_V) application_top_auto $(AUTOGEN_DIR)/moving_average.v oscope_marble2_features +oscope_top.sv: $(AUTOGEN_DIR)/config_romx.v $(RTEFI_V) $(AUTOGEN_DIR)/application_top_auto.vh $(AUTOGEN_DIR)/addr_map_application_top.vh $(AUTOGEN_DIR)/moving_average.v oscope_marble2_features oscope_top.bit: $(AUTOGEN_DIR)/config_romx.v +# ===== +# Process code base with sv2v, verilator, yosys, and cdc_snitch +# Experimental and still somewhat messy +# Exposes weaknessess in our tools' handling of inout ports; see +# https://github.com/zachjs/sv2v/issues/295 +# https://github.com/YosysHQ/yosys/issues/4708 +# https://github.com/verilator/verilator/issues/2844 +UPPER_SV = oscope_top.sv $(OSCOPE_COMMON_DIR)/application_top.sv $(BOARD_SUPPORT_DIR)/zest/zest_cfg_if.sv $(BOARD_SUPPORT_DIR)/zest/zest_if.sv $(BOARD_SUPPORT_DIR)/zest/zest_wrap.sv $(OSCOPE_COMMON_DIR)/digitizer_config.sv +oscope_prep.v: $(AUTOGEN_DIR)/application_top_auto.vh $(AUTOGEN_DIR)/addr_map_application_top.vh oscope_marble2_features + $(SV2V) -DSIMULATE -DVERILATOR -I $(AUTOGEN_DIR) $(UPPER_SV) > $@ + wc -l $@ +oscope_pure_v.d: oscope_prep.v $(AUTOGEN_DIR)/config_romx.v $(RTEFI_V) $(AUTOGEN_DIR)/moving_average.v + $(VERILOG) -Wno-timescale -o /dev/null $< -y$(AUTOGEN_DIR) $(VFLAGS_DEP) -M$@.$$$$ && sort -u < $@.$$$$ | tr '\n' ' ' | sed -e 's/^/oscope_pure_v_check oscope_prep_yosys.json: /' -e 's/ $$//' > $@ && rm $@.$$$$ +# make this dependency file first, explicitly, if you are doing sv2v/cdc_snitch work +dep: oscope_pure_v.d +-include oscope_pure_v.d +.PHONY: oscope_pure_v_check dep +# dependencies from oscope_pure_v.d +# sv2v tends to make a lot of VARHIDDEN +# I don't like the 33 x UNDRIVEN +VLATOR_LINT_IGNORE += -Wno-UNUSED -Wno-VARHIDDEN -Wno-DECLFILENAME -Wno-CASEINCOMPLETE +VLATORFLAGS += -DSIMULATE +oscope_pure_v_check: + $(VERILATOR_LINT) +# exercise with make dep && make oscope_pure_v_check +# or make dep && make oscope_prep_lint +# same result, but different dependency list management +# +YOSYS_JSON_OPTION += -DSIMULATE +# most dependencies from oscope_pure_v.d +oscope_prep_yosys.json: $(FPGA_FAMILY_DIR)/xilinx/IBUFGDS.v +# exercise with make dep && make oscope_prep_cdc.txt +# ===== + $(AUTOGEN_DIR)/config_romx.v: $(BUILD_DIR)/build_rom.py $(APP_NAME)_regmap.json $(PYTHON) $< -v $@ -j $(APP_NAME)_regmap.json @@ -71,12 +106,13 @@ $(APP_NAME)_regmap.json: $(AUTOGEN_DIR)/regmap_application_top.json scalar_$(APP $(PYTHON) $(OSCOPE_COMMON_DIR)/shorten_names.py -o $@ -i $(APP_NAME)_regmap_long.json download: - openocd -f ../../test_marble_family/marble.cfg -c "transport select jtag; init; xc7_program xc7.tap; pld load 0 oscope_top.bit; exit" + openocd -f $(MARBLE_FAMILY_DIR)/marble.cfg -c "transport select jtag; init; xc7_program xc7.tap; pld load 0 oscope_top.bit; exit" include $(BUILD_DIR)/bottom_rules.mk CLEAN += *.bit *.bin *.prm $(APP_NAME)_regmap*.json scalar_$(APP_NAME)_regmap.json *_features*.json *_features*.vh oscope_marble*_features -CLEAN += system_top.xdc zest_connector.csv $(RTEFI_CLEAN) +CLEAN += system_top.xdc zest_connector.csv $(RTEFI_CLEAN) foo.gtkw foo.vcd +CLEAN += oscope_prep.v oscope_prep_yosys.json oscope_prep_cdc.txt oscope_pure_v.d CLEAN_DIRS += _xilinx .Xil ifneq (,$(findstring bit,$(MAKECMDGOALS))) diff --git a/projects/oscope/marble_family/README.md b/projects/oscope/marble_family/README.md index c4c0e4562..9001be97a 100644 --- a/projects/oscope/marble_family/README.md +++ b/projects/oscope/marble_family/README.md @@ -54,7 +54,7 @@ With below ADC clock domain can be set to 100MHz LMK dividers in zest_setup.py for zest are set to divide the below 1400MHz down to 100MHz [This can be changed Inside zest_setup.py .. good luck] For waveforms, using Si***_loader from: -https://github.com/yetifrisstlama/Si5xx-5x7-EVV_autoloader +https://github.com/michael-betz/Si5xx-5x7-EVV_autoloader ``` python setFreq.py -p /dev/ttyUSB2 156.25e6 1400e6 ``` diff --git a/projects/oscope/marble_family/oscope_top.sv b/projects/oscope/marble_family/oscope_top.sv index b865e6c27..65b8dc170 100644 --- a/projects/oscope/marble_family/oscope_top.sv +++ b/projects/oscope/marble_family/oscope_top.sv @@ -149,9 +149,12 @@ gmii_to_rgmii #(.in_phase_tx_clk(in_phase_tx_clk)) gmii_to_rgmii_i( ); wire BOOT_CCLK; +wire cfg_clk; // Just for fun, so we can measure its frequency `ifndef SIMULATE -STARTUPE2 set_cclk(.USRCCLKO(BOOT_CCLK), .USRCCLKTS(1'b0)); -`endif // `ifndef SIMULATE +STARTUPE2 set_cclk(.USRCCLKO(BOOT_CCLK), .USRCCLKTS(1'b0), .CFGMCLK(cfg_clk)); +`else +assign cfg_clk = 0; +`endif // Placeholders wire ZEST_PWR_EN; @@ -181,6 +184,25 @@ wire [1:0] FMC2_CK_N; wire [23:0] FMC2_HA_P; wire [23:0] FMC2_HA_N; +wire si570; +`ifdef USE_SI570 +// Single-ended clock derived from programmable xtal oscillator +ds_clk_buf #( + .GTX (1)) +i_ds_gtrefclk1 ( + .clk_p (GTREFCLK_P), + .clk_n (GTREFCLK_N), + .clk_out (si570) +); +`else +assign si570 = 0; +`endif + +// Works +reg bad_slow_clock=0; +always @(posedge tx_clk) bad_slow_clock <= ~bad_slow_clock; +wire clk62 = bad_slow_clock; + // Real, portable implementation // Consider pulling 3-state drivers out of this marble_base #( @@ -192,11 +214,12 @@ marble_base #( .vgmii_tx_en(vgmii_tx_en), .vgmii_tx_er(vgmii_tx_er), .vgmii_rx_clk(vgmii_rx_clk), .vgmii_rxd(vgmii_rxd), .vgmii_rx_dv(vgmii_rx_dv), .vgmii_rx_er(vgmii_rx_er), - .phy_rstn(PHY_RSTN), .clk_locked(clk_locked), + .phy_rstn(PHY_RSTN), .clk_locked(clk_locked), .si570(si570), .boot_clk(BOOT_CCLK), .boot_cs(BOOT_CS_B), .boot_mosi(BOOT_MOSI), .boot_miso(BOOT_MISO), .cfg_d02(CFG_D02), .mmc_int(MMC_INT), .ZEST_PWR_EN(ZEST_PWR_EN), - .aux_clk(SYSCLK_P), .GPS(4'b0), + .aux_clk(SYSCLK_P), .clk62(clk62), .cfg_clk(cfg_clk), + .GPS(4'b0), .SCLK(SCLK), .CSB(CSB), .MOSI(MOSI), .MISO(MISO), .FPGA_RxD(FPGA_RxD), .FPGA_TxD(FPGA_TxD), .twi_scl({dum_scl, old_scl1, old_scl2, TWI_SCL}), diff --git a/projects/oscope/software/bmb7/ctrace_dump.py b/projects/oscope/software/bmb7/ctrace_dump.py index 6909d6b0b..f2666d27d 100644 --- a/projects/oscope/software/bmb7/ctrace_dump.py +++ b/projects/oscope/software/bmb7/ctrace_dump.py @@ -6,7 +6,7 @@ def tobin(x, count=8): # Integer to binary; count is number of bits # Some credit to W.J. van der Laan in - # http://code.activestate.com/recipes/219300/ + # https://code.activestate.com/recipes/219300/ return [(x >> y) & 1 for y in range(count-1, -1, -1)] diff --git a/projects/test_marble_family/Makefile b/projects/test_marble_family/Makefile index 602e2189b..9791dd4bb 100644 --- a/projects/test_marble_family/Makefile +++ b/projects/test_marble_family/Makefile @@ -41,7 +41,7 @@ vpath %.c $(BADGER_DIR)/tests $(BUILD_DIR) include $(HOMELESS_DIR)/freq_demo/freq_demo_rules.mk # This must go in front of include $(BADGER_DIR)/rules.mk -all: lb_marble_slave_tb marble_base_tb no_multiple_drivers_check marble_base_lint gps_test_tb ltm_sync_tb Vmarble_base dna_check +all: lb_marble_slave_tb marble_base_tb no_multiple_drivers_check marble_base_lint gps_test_tb ltm_sync_tb Vmarble_base dna_check mmc_mailbox_check # ===== # gps_test, too simple to need testing ... right diff --git a/projects/test_marble_family/README.md b/projects/test_marble_family/README.md index d747d4a5e..110a86526 100644 --- a/projects/test_marble_family/README.md +++ b/projects/test_marble_family/README.md @@ -7,7 +7,7 @@ The Marble.xdc file here is the one generated by a script in that repository. Infrastructure provided by [Bedrock](https://github.com/BerkeleyLab/Bedrock). -Currently tested successful at booting a bitfile (using [openocd](http://openocd.org/)), +Currently tested successful at booting a bitfile (using [openocd](https://openocd.org/)), bringing up Ethernet (synthesized default IP address 192.168.19.10, but that's normally overridden by the on-board MMC), blinking LEDs, and reading/writing/booting SPI Flash (using Bedrock's spi_test.py). diff --git a/projects/test_marble_family/bringup.txt b/projects/test_marble_family/bringup.txt index 43fc38060..dc624107f 100644 --- a/projects/test_marble_family/bringup.txt +++ b/projects/test_marble_family/bringup.txt @@ -78,7 +78,7 @@ cmp /tmp/f2 /tmp/c2 # LPC1776, seems to work OK using OpenOCD from either git master # or stock Debian Buster. -git clone https://github.com/richardeoin/lpc-toolchain.git +git clone https://github.com/richardeoin/lpc-toolchain git checkout LPC1769 patch -p1 < marble-lpc1776.patch # apt-get install gcc-arm-none-eabi diff --git a/projects/test_marble_family/config_si570.py b/projects/test_marble_family/config_si570.py index 91ebe6db9..0f194dc92 100644 --- a/projects/test_marble_family/config_si570.py +++ b/projects/test_marble_family/config_si570.py @@ -16,7 +16,7 @@ from time import sleep -def decode_settings(mbox, verbose): +def decode_settings(mbox, verbose=False): for page in range(7): subset = mbox[page*16:page*16+16] if verbose: @@ -37,11 +37,11 @@ def decode_settings(mbox, verbose): i2c_addr = mbox[96] config = mbox[97] start_freq = int.from_bytes(bytes(mbox[98:102]), 'big') # 4 bytes, MSB-first, unused - if ((i2c_addr == 0) or (i2c_addr == 0xff) or (config == 0) or (config == 0xff) or (pcb_rev == 0xdeadbeef)): + if (i2c_addr == 0) or (i2c_addr == 0xff) or (config == 0) or (config == 0xff) or (pcb_rev == 0xdeadbeef): print("SI570 settings not configured through MMC,using default for {:s} v1.{:d}".format(board_name, marble_rev)) start_freq = 0 # use default values if it's a marble v1.2, v1.3 or marble_mini - if (marble_rev == 2 or marble_rev == 3 or board == 2): + if marble_rev == 2 or marble_rev == 3 or board == 2: i2c_addr = 0xee polarity = 0 start_addr = 0x0d @@ -50,7 +50,7 @@ def decode_settings(mbox, verbose): polarity = 0 start_addr = 0x07 # check the [7:6] bits of the config value is either 2'b01 or 2'b10, to make sure it valid - elif (((config >> 6) == 1) ^ ((config >> 6) == 2)): + elif ((config >> 6) == 1) ^ ((config >> 6) == 2): start_addr = 0x0d if (config & 0x02) else 0x07 polarity = config & 0x01 else: @@ -164,19 +164,19 @@ def hw_write_prog(si570_addr, start_addr, reg): # check if the final output frequency is <= 50 ppm -def check(fin): - ppm = ((fin)*(1/args.new_freq) - 1.0)*1e6 - if (abs(ppm) >= 50): +def check(fin, new_freq): + ppm = ((fin)*(1/new_freq) - 1.0)*1e6 + if abs(ppm) >= 50: raise ValueError('SI570 final frequency measurement is not correct, out of spec by %i ppm' % ppm) -def compute_si570(addr, key, verbose): +def compute_si570(addr, key, verbose=False, debug=False): mbox = addr.reg_read(["spi_mbox"])[0] # using keyword just to keep print consistent _, si570_addr, polarity, config_addr, _ = decode_settings(mbox, verbose) prog = hw_test_prog(si570_addr, polarity, config_addr) - result = testcase.run_testcase(addr, prog, result_len=359, debug=args.debug, verbose=verbose) - if args.debug: + result = testcase.run_testcase(addr, prog, result_len=359, debug=debug, verbose=verbose) + if debug: print(" ".join(["%2.2x" % p for p in prog])) print("") for jx in range(16): @@ -195,7 +195,7 @@ def compute_si570(addr, key, verbose): # keep everything in MHz fdco = default * n1 * hs_div fxtal = fdco / rfreq - if args.verbose: + if verbose: print('%s SI570 settings:' % key) print('REFREQ: %4.4f' % rfreq) print('N1: %3d' % n1) @@ -208,15 +208,15 @@ def compute_si570(addr, key, verbose): return si570_addr, config_addr, fxtal, default -def config_si570(addr, verbose): - if args.new_freq: - si570_addr, config_addr, fxtal, default = compute_si570(addr, "Measured", verbose) +def config_si570(addr, new_freq, verbose=False, debug=False): + if new_freq: + si570_addr, config_addr, fxtal, default = compute_si570(addr, "Measured", verbose, debug) # if first measured frequency and new output frequency are < 10 ppm don't change/update - if (abs(((default)*(1/args.new_freq) - 1.0)*1e6) < 10): + if abs(((default)*(1/new_freq) - 1.0)*1e6) < 10: pass else: print("#######################################") - print("Changing output frequency to %4.4f MHz" % args.new_freq) + print("Changing output frequency to %4.4f MHz" % new_freq) # DCO frequency range: 4850 - 5670MHz # HSDIV values: 4, 5, 6, 7, 9 or 11 (subtract 4 to store) # N1 values: 1, 2, 4, 6, 8...128 @@ -227,7 +227,7 @@ def config_si570(addr, verbose): if i == 0: n1_i = 1 for hsdiv_i in [4, 5, 6, 7, 9, 11]: - fdco_i = args.new_freq * n1_i * hsdiv_i + fdco_i = new_freq * n1_i * hsdiv_i if (fdco_i > 4850.0) and (fdco_i < 5670.0): # print(n1_i-1, hsdiv_i-4, fdco_i) if fdco_i < best[2]: @@ -236,7 +236,7 @@ def config_si570(addr, verbose): if best[2] > 5700.0: raise Exception('Could not find appropriate settings for your new target frequency') - if args.debug: + if debug: print('New best option is:') print(best[0]-1, best[1]-4, best[2]) @@ -261,8 +261,8 @@ def config_si570(addr, verbose): # write new registers reg = [reg7, reg8, reg9, reg10, reg11, reg12] chg = hw_write_prog(si570_addr, config_addr, reg) - result1 = testcase.run_testcase(addr, chg, result_len=359, debug=args.debug, verbose=verbose) - if args.debug: + result1 = testcase.run_testcase(addr, chg, result_len=359, debug=debug, verbose=verbose) + if debug: print(" ".join(["%2.2x" % p for p in chg])) print("") for jx in range(16): @@ -272,11 +272,11 @@ def config_si570(addr, verbose): sleep(1) # read final values and output frequency? print("#######################################") - _, _, _, freq = compute_si570(addr, "Final", verbose) - check(freq) + _, _, _, freq = compute_si570(addr, "Final", verbose, debug) + check(freq, new_freq) else: # read only current settings if you don't want to change anything print("#######################################") - compute_si570(addr, "Measured", verbose) + compute_si570(addr, "Measured", verbose, debug) if __name__ == "__main__": @@ -297,7 +297,7 @@ def config_si570(addr, verbose): # dev = lbus_access.lbus_access(args.addr, port=args.port, timeout=3.0, allow_burst=False) - config_si570(addr, args.verbose) + config_si570(addr, args.new_freq, args.verbose, args.debug) # usage: # To read current output frequency: diff --git a/projects/test_marble_family/ctrace_dump_gps.py b/projects/test_marble_family/ctrace_dump_gps.py index c8b72bc11..ea1ea5bd4 100644 --- a/projects/test_marble_family/ctrace_dump_gps.py +++ b/projects/test_marble_family/ctrace_dump_gps.py @@ -5,7 +5,7 @@ def tobin(x, count=8): # Integer to binary; count is number of bits # Some credit to W.J. van der Laan in - # http://code.activestate.com/recipes/219300/ + # https://code.activestate.com/recipes/219300/ return [(x >> y) & 1 for y in range(count-1, -1, -1)] diff --git a/projects/test_marble_family/first_readout.sh b/projects/test_marble_family/first_readout.sh index 8288ce30b..bf607bf9f 100644 --- a/projects/test_marble_family/first_readout.sh +++ b/projects/test_marble_family/first_readout.sh @@ -9,7 +9,7 @@ echo "Reading kintex 7 internal temperature for $IP using XADC" python3 -m xadctemp -a $IP -p 803 echo "Reading kintex 7 DNA for $IP" python3 -m leep.cli leep://$IP:803 reg dna_high dna_low -echo "Connect Digilent 8 LED board to PMOD J12 and check if all them blink at different rate" +echo "Connect Digilent 8 LED board to Pmod J12 and check if all them blink at different rate" python3 -m leep.cli leep://$IP:803 reg led_user_mode=2 tt=$(mktemp /tmp/quick_XXXXXX) python3 -m spi_test --ip $IP --udp 804 --otp --pages=1 --dump $tt diff --git a/projects/test_marble_family/fmc_test_iam.py b/projects/test_marble_family/fmc_test_iam.py index 0caf682d3..de14623b8 100644 --- a/projects/test_marble_family/fmc_test_iam.py +++ b/projects/test_marble_family/fmc_test_iam.py @@ -1,5 +1,5 @@ # PoC Marble FMC tester based on IAM Electronic FPGA Mezzanine Card (FMC) Loopback Module -# https://www.iamelectronic.com/shop/produkt/fpga-mezzanine-card-fmc-loopback-module +# https://www.iamelectronic.com/shop/produkt/fpga-mezzanine-card-fmc-loopback-module/ # Still doesn't cover LA_1 to GBTCLK0_M2C or LA_18 to GBTCLK1_M2C (HPC) import sys @@ -12,7 +12,7 @@ def tobin(x, count=8): # Integer to binary; count is number of bits - # Credit to W.J. van der Laan in http://code.activestate.com/recipes/219300/ + # Credit to W.J. van der Laan in https://code.activestate.com/recipes/219300/ return "".join([str((x >> y) & 1) for y in range(count-1, -1, -1)]) diff --git a/projects/test_marble_family/fmc_test_l.py b/projects/test_marble_family/fmc_test_l.py index 0acbde687..c31a7fd2d 100644 --- a/projects/test_marble_family/fmc_test_l.py +++ b/projects/test_marble_family/fmc_test_l.py @@ -1,5 +1,5 @@ # Mapping functions to support CERN's OHWR FMC Carrier Tester board -# https://www.ohwr.org/project/fmc-conn-tester/wikis/home +# https://ohwr.org/project/fmc-conn-tester/-/wikis/home # a703f7f70230f579a87bd9c17df9f9591a629c91d9f8a5fc84de695a3bcf9ae3 EDA-02327-V1-0_sch.pdf # specifically its connectivity between the LA bank and I2C port expanders @@ -105,7 +105,7 @@ def tobin(x, count=8): # Integer to binary; count is number of bits - # Props to W.J. van der Laan in http://code.activestate.com/recipes/219300/ + # Props to W.J. van der Laan in https://code.activestate.com/recipes/219300/ return list(map(lambda y: (x >> y) & 1, range(count-1, -1, -1))) diff --git a/projects/test_marble_family/i2c/Makefile b/projects/test_marble_family/i2c/Makefile index e9ff48874..9616b851e 100644 --- a/projects/test_marble_family/i2c/Makefile +++ b/projects/test_marble_family/i2c/Makefile @@ -1,14 +1,14 @@ # A demo of Marble-specific I2C functionality -PYTHON=python3 +PYTHON = python3 -TARGETS=prog.dat prog.vh prog.h prog.json +TARGETS = prog.dat prog.vh prog.h prog.json .PHONY: all all: $(TARGETS) # ======= Build a program for i2c_chunk -PROG_FILE=demo_marble_i2c.py +PROG_FILE = demo_marble_i2c.py prog.dat: $(PROG_FILE) $(PYTHON) $< > $@ @@ -25,13 +25,13 @@ prog.json: $(PROG_FILE) $(PYTHON) $< j 0x800 > $@ # ======= Test a bunch of assembler violations -BAD_FILE=demo_i2c_baddy.py +BAD_FILE = demo_i2c_baddy.py .PHONY: test test: $(BAD_FILE) $(PYTHON) $< # ======= Decode a program in marble context -DECODE_FILE=marble_i2c_decoder.py +DECODE_FILE = marble_i2c_decoder.py .PHONY: decode decode: prog.dat $(PYTHON) $(DECODE_FILE) $< diff --git a/projects/test_marble_family/logpath.py b/projects/test_marble_family/logpath.py index bf15fb654..86ffbaad4 100644 --- a/projects/test_marble_family/logpath.py +++ b/projects/test_marble_family/logpath.py @@ -52,10 +52,12 @@ def main(args): import argparse parser = argparse.ArgumentParser(description="Marble test data log file path exporter") parser.add_argument("serial_number", help="Marble board serial number") - parser.add_argument('-b', '--base', default=None, + parser.add_argument( + '-b', '--base', default=None, help="Base logfile directory (attempts to use $MARBLE_LOGPATH by default)") parser.add_argument('-v', '--version', default=None, help="Marble board version (e.g. 1.2, 1.3, 1.4, 1.4.1, etc)") - parser.add_argument('-a', '--absolute', default=False, action="store_true", + parser.add_argument( + '-a', '--absolute', default=False, action="store_true", help="Return an absolute path instead of a relative one.") args = parser.parse_args() import sys diff --git a/projects/test_marble_family/marble.tcl b/projects/test_marble_family/marble.tcl index ac3fbb624..b25ff87b1 100644 --- a/projects/test_marble_family/marble.tcl +++ b/projects/test_marble_family/marble.tcl @@ -67,7 +67,7 @@ proc project_rpt {project_name} { report_datasheet -v -file ./_xilinx/$project_name/imp_datasheet.rpt report_cdc -v -details -file ./_xilinx/$project_name/cdc_report.rpt report_timing_summary -delay_type min_max -report_unconstrained -check_timing_verbose -max_paths 10 -input_pins -file ./_xilinx/$project_name/imp_timing.rpt - # http://xillybus.com/tutorials/vivado-timing-constraints-error + # https://xillybus.com/tutorials/vivado-timing-constraints-error if {! [string match -nocase {*timing constraints are met*} [report_timing_summary -no_header -no_detailed_paths -return_string]]} { puts "Timing constraints weren't met. Please check your design." exit 2 diff --git a/projects/test_marble_family/nmea_view.py b/projects/test_marble_family/nmea_view.py index 9dd8ec5dc..31d3429f2 100644 --- a/projects/test_marble_family/nmea_view.py +++ b/projects/test_marble_family/nmea_view.py @@ -8,7 +8,7 @@ # helpful hints at -# http://aprs.gids.nl/nmea/#gsv +# https://aprs.gids.nl/nmea/#gsv def handle_gsv(gsv_state, a): global sats s_nmsg, s_imsg, s_nsat = gsv_state @@ -70,7 +70,7 @@ def nmea_line(gsv_state, ll, verbose=False): if aa[0] == "GPGSV": gsv_state = handle_gsv(gsv_state, aa[1:]) if aa[0] == "GPGGA" and len(aa) > 6: - # helpful hints at http://aprs.gids.nl/nmea/#gga + # helpful hints at https://aprs.gids.nl/nmea/#gga # note the weird time format: "060534.000" means # 06:05:34.000 Z # note the weird lat/lon format: "3752.6812 N" means @@ -82,7 +82,7 @@ def nmea_line(gsv_state, ll, verbose=False): pl = utc_time, lat, aa[3], lon, aa[5] print("GPS time %s coordinates %.6f %s, %.6f %s" % pl) if aa[0] == "GPRMC" and len(aa) > 9: - # helpful hints at http://aprs.gids.nl/nmea/#rmc + # helpful hints at https://aprs.gids.nl/nmea/#rmc # same weird formats as above, plus date "111222" means 2022-12-11 gt = aa[1] utc_time = gt[0:2] + ":" + gt[2:4] + ":" + gt[4:] + " Z" diff --git a/projects/test_marble_family/testcase.py b/projects/test_marble_family/testcase.py index 721643a45..a46a14cd0 100644 --- a/projects/test_marble_family/testcase.py +++ b/projects/test_marble_family/testcase.py @@ -5,7 +5,6 @@ sys.path.append(bedrock_dir + "peripheral_drivers/i2cbridge") sys.path.append(bedrock_dir + "badger") sys.path.append(bedrock_dir + "projects/common") -import leep from c2vcd import produce_vcd from fmc_test_l import fmc_decode @@ -352,6 +351,7 @@ def print_result(result, args, board_type, si570_dfreq, poll_only=False): # OK, setup is finished, start the actual work # dev = lbus_access.lbus_access(addr, port=port, timeout=3.0, allow_burst=False) + import leep leep_addr = "leep://" + addr + ":" + str(port) print(leep_addr) dev = leep.open(leep_addr, timeout=5.0) diff --git a/projects/test_marble_family/xadctemp.py b/projects/test_marble_family/xadctemp.py index 8a00945da..5fd7ab0c2 100644 --- a/projects/test_marble_family/xadctemp.py +++ b/projects/test_marble_family/xadctemp.py @@ -1,7 +1,11 @@ #!/usr/bin/python3 # Convert xadc internal temperature register value (leep xadc_internal_temperature) # to degrees Celsius. +import sys import re +bedrock_dir = "../../" +sys.path.append(bedrock_dir + "projects/common") +import leep def _allhex(s): @@ -102,13 +106,11 @@ def doLeep(ipaddr, port): if __name__ == "__main__": - import sys import argparse parser = argparse.ArgumentParser( description="Utility to read internal temperature of ") parser.add_argument('-a', '--addr', required=True, help='IP address (required)') parser.add_argument('-p', '--port', type=int, default=803, help='Port number (default 803)') - import leep args = parser.parse_args() sys.exit(doLeep(args.addr, args.port)) diff --git a/projects/trigger_capture/Makefile b/projects/trigger_capture/Makefile index 75b41c0b9..938967993 100644 --- a/projects/trigger_capture/Makefile +++ b/projects/trigger_capture/Makefile @@ -1,11 +1,12 @@ +PYTHON = python3 marble.bit: echo "dummy" > firmware/app.bin - python3 marble.py --no-compile-gateware --csr-csv csr.csv --cpu-type picorv32 --uart-name crossover + $(PYTHON) marble.py --no-compile-gateware --csr-csv csr.csv --cpu-type picorv32 --uart-name crossover make -C firmware clean all - python3 marble.py --build --csr-csv csr.csv --cpu-type picorv32 --uart-name crossover+uartbone + $(PYTHON) marble.py --build --csr-csv csr.csv --cpu-type picorv32 --uart-name crossover+uartbone load: - python3 marble.py --load --no-compile-gateware --no-compile-software + $(PYTHON) marble.py --load --no-compile-gateware --no-compile-software clean: rm -rf build diff --git a/projects/trigger_capture/capture.py b/projects/trigger_capture/capture.py index 129d9db4a..e0e30cd26 100644 --- a/projects/trigger_capture/capture.py +++ b/projects/trigger_capture/capture.py @@ -15,7 +15,7 @@ def trigger_hardware(n_points, cap_ip, cap_port, - csr_csv = None): + csr_csv = None): wb = RemoteClient(csr_csv=csr_csv) wb.open() diff --git a/projects/trigger_capture/data_pipe.py b/projects/trigger_capture/data_pipe.py index 04d1d6fd0..e167d67f8 100644 --- a/projects/trigger_capture/data_pipe.py +++ b/projects/trigger_capture/data_pipe.py @@ -37,7 +37,8 @@ def udp_fragmenter_description(dw): class UDPFragmenterPacketizer(Packetizer): def __init__(self, dw=8): - Packetizer.__init__(self, + Packetizer.__init__( + self, udp_fragmenter_description(dw), eth_udp_user_description(dw), fragmenter_header) @@ -82,10 +83,10 @@ def __init__(self, dw=8): counter_ce = Signal() self.sync += \ If(counter_reset, - counter.eq(0) - ).Elif(counter_ce, - counter.eq(counter + ww) - ) + counter.eq(0) + ).Elif(counter_ce, + counter.eq(counter + ww) + ) bytes_in_fragment = Signal(16, reset=0) self.submodules.fsm = fsm = FSM(reset_state="IDLE") @@ -96,7 +97,7 @@ def __init__(self, dw=8): sink.connect(packetizer.sink, omit={"length"}), # TODO source.length.eq(sink.length) - ).Else( + ).Else( sink.ready.eq(0), source.length.eq(UDP_FRAG_MTU + 8), counter_reset.eq(1), @@ -107,8 +108,8 @@ def __init__(self, dw=8): NextValue(bytes_in_fragment, UDP_FRAG_MTU), NextState("FRAGMENTED_PACKET_SEND") ) + ) ) - ) fsm.act("FRAGMENTED_PACKET_SEND", sink.connect(packetizer.sink, omit={"length"}), @@ -116,7 +117,7 @@ def __init__(self, dw=8): source.length.eq(bytes_in_fragment + 8), If(sink.valid & packetizer.sink.ready, counter_ce.eq(1) - ), + ), If(counter == (bytes_in_fragment - ww), NextValue(fragment_offset, fragment_offset + (bytes_in_fragment >> 3)), @@ -124,11 +125,11 @@ def __init__(self, dw=8): If(((fragment_offset << 3) + counter + ww) == sink.length, NextValue(fragment_offset, 0), NextState("IDLE") - ).Else( + ).Else( counter_ce.eq(0), NextState("NEXT_FRAGMENT")) + ) ) - ) fsm.act("NEXT_FRAGMENT", counter_ce.eq(0), @@ -139,14 +140,14 @@ def __init__(self, dw=8): counter_reset.eq(1), If((sink.length - (fragment_offset << 3)) > UDP_FRAG_MTU, NextValue(bytes_in_fragment, UDP_FRAG_MTU), - ).Else( + ).Else( NextValue(bytes_in_fragment, sink.length - (fragment_offset << 3)), NextValue(mf, 0), ), NextValue(fragment_id, fragment_id + 1), NextState("FRAGMENTED_PACKET_SEND") - ) + ) class Counter(Module): @@ -206,7 +207,7 @@ def __init__(self, ddr_wr_port, ddr_rd_port, udp_port, adc_source, adc_dw): If(adc_source.valid, adc_data.eq(Cat(adc_data[adc_dw:], adc_source.data)), word_count.eq(word_count + 1) - ), + ), word_count_d.eq(word_count), ] @@ -221,16 +222,16 @@ def __init__(self, ddr_wr_port, ddr_rd_port, udp_port, adc_source, adc_dw): If(self.fifo_load.re & self.fifo_load.storage, fifo_counter.eq(0), load_fifo.eq(1) - ), + ), If(load_fifo & adc_source.valid, self.fifo_full.status.eq(0), self.fifo_error.status.eq(~dram_fifo.dram_fifo.ctrl.writable), fifo_counter.eq(fifo_counter + 1) - ), + ), If((fifo_counter == fifo_size - 1) & adc_source.valid, load_fifo.eq(0), self.fifo_full.status.eq(1) - ), + ), ] # fifo --> stride converter @@ -244,9 +245,9 @@ def __init__(self, ddr_wr_port, ddr_rd_port, udp_port, adc_source, adc_dw): self.sync += [ If(dram_fifo.source.valid & dram_fifo.source.ready, receive_count.eq(receive_count + 1) - ).Elif(read_from_dram_fifo == 0, - receive_count.eq(0) - ) + ).Elif(read_from_dram_fifo == 0, + receive_count.eq(0) + ) ] # --> udp fragmenter --> self.submodules.udp_fragmenter = udp_fragmenter = UDPFragmenter(udp_port.dw) @@ -315,16 +316,16 @@ def __init__(self, ddr_wr_port, ddr_rd_port, udp_port): If(self.fifo_load.re & self.fifo_load.storage, fifo_counter.eq(0), load_fifo.eq(1) - ), + ), If(load_fifo & adcs.source.valid, self.fifo_full.status.eq(0), self.fifo_error.status.eq(~dram_fifo.dram_fifo.ctrl.writable), fifo_counter.eq(fifo_counter + 1) - ), + ), If((fifo_counter == fifo_size - 1) & adcs.source.valid, load_fifo.eq(0), self.fifo_full.status.eq(1) - ), + ), ] self.comb += [ @@ -344,9 +345,9 @@ def __init__(self, ddr_wr_port, ddr_rd_port, udp_port): self.sync += [ If(dram_fifo.source.valid & dram_fifo.source.ready, receive_count.eq(receive_count + 1) - ).Elif(read_from_dram_fifo == 0, - receive_count.eq(0) - ) + ).Elif(read_from_dram_fifo == 0, + receive_count.eq(0) + ) ] # --> udp fragmenter --> self.submodules.udp_fragmenter = udp_fragmenter = UDPFragmenter(udp_port.dw) diff --git a/projects/trigger_capture/firmware/build.py b/projects/trigger_capture/firmware/build.py index 161cf9bc4..0131f1498 100755 --- a/projects/trigger_capture/firmware/build.py +++ b/projects/trigger_capture/firmware/build.py @@ -26,8 +26,9 @@ def main(): # Compile app build_path = args.build_path if os.path.isabs(args.build_path) else os.path.join("..", args.build_path) print(args.app_dir_path) - os.system(f"export BUILD_DIR={build_path} && export APP_DIR={args.app_dir_path} &&" + - "{'export WITH_CXX=1 &&' if args.with_cxx else ''} cd build && make") + os.system( + f"export BUILD_DIR={build_path} && export APP_DIR={args.app_dir_path} &&" + + "{'export WITH_CXX=1 &&' if args.with_cxx else ''} cd build && make") # Copy demo.bin os.system("cp build/app.bin ./") diff --git a/projects/trigger_capture/marble.py b/projects/trigger_capture/marble.py index 3d0a77864..8d36dd687 100644 --- a/projects/trigger_capture/marble.py +++ b/projects/trigger_capture/marble.py @@ -66,8 +66,7 @@ def add_zest(self): self.platform.lookup_request("ZEST_CLK_TO_FPGA", 1, loose=True).p, self.platform.lookup_request("ZEST_ADC_DCO", 0, loose=True).p, self.platform.lookup_request("ZEST_ADC_DCO", 1, loose=True).p, - self.platform.lookup_request("ZEST_DAC_DCO", loose=True).p - ) + self.platform.lookup_request("ZEST_DAC_DCO", loose=True).p) # self.dsp_clk_out = Signal() # self.clk_div_out = Signal(2) diff --git a/projects/trigger_capture/platforms/marble.py b/projects/trigger_capture/platforms/marble.py index 1244c736e..c5be9b7ba 100644 --- a/projects/trigger_capture/platforms/marble.py +++ b/projects/trigger_capture/platforms/marble.py @@ -14,7 +14,7 @@ # https://github.com/BerkeleyLab/Marble # # Generated by gen_marble.py (git 4a42959) -# https://github.com/yetifrisstlama/litex_test_project/blob/4a42959/xdc/gen_marble.py +# https://github.com/michael-betz/litex_test_project/blob/4a42959/xdc/gen_marble.py # Pin numbers extracted from a .xdc file, which was auto-generated from # the KiCad Schematic. diff --git a/projects/trigger_capture/targets/marble.py b/projects/trigger_capture/targets/marble.py index 11ca96ca3..397b36b36 100644 --- a/projects/trigger_capture/targets/marble.py +++ b/projects/trigger_capture/targets/marble.py @@ -102,7 +102,8 @@ def __init__( platform = marble.Platform() # SoCCore ---------------------------------------------------------------------------------- - SoCCore.__init__(self, platform, sys_clk_freq, + SoCCore.__init__( + self, platform, sys_clk_freq, ident = "LiteX SoC on Marble", **kwargs) diff --git a/rtsim/cav_elec.v b/rtsim/cav_elec.v index 84f51c139..dfced2bf1 100644 --- a/rtsim/cav_elec.v +++ b/rtsim/cav_elec.v @@ -61,6 +61,7 @@ module cav_elec( input [11:0] modulo, // external `AUTOMATIC_self ); +`undef AUTOMATIC_self `AUTOMATIC_decode `AUTOMATIC_map diff --git a/rtsim/cav_mech.v b/rtsim/cav_mech.v index fa0e77f7e..ee6f10d16 100644 --- a/rtsim/cav_mech.v +++ b/rtsim/cav_mech.v @@ -17,6 +17,7 @@ module cav_mech( output res_clip, `AUTOMATIC_self ); +`undef AUTOMATIC_self `AUTOMATIC_decode @@ -62,4 +63,5 @@ always @(posedge clk) begin end assign environment = noise_out; +`undef AUTOMATIC_prng endmodule // cav_mech diff --git a/rtsim/cav_mode.v b/rtsim/cav_mode.v index 250b7996b..ad5face23 100644 --- a/rtsim/cav_mode.v +++ b/rtsim/cav_mode.v @@ -62,6 +62,7 @@ module cav_mode( input signed [17:0] bw, // external `AUTOMATIC_self ); +`undef AUTOMATIC_self `AUTOMATIC_decode diff --git a/rtsim/param.py b/rtsim/param.py index fedb0edf3..1e0006f03 100644 --- a/rtsim/param.py +++ b/rtsim/param.py @@ -2,7 +2,7 @@ from numpy import exp as cexp from numpy import ceil -# http://stackoverflow.com/questions/14132789/python-relative-imports-for-the-billionth-time +# https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time # Leaves me with only one choice ... :( # Since I don't want to modify shell variables import os diff --git a/rtsim/station.v b/rtsim/station.v index 46d2287b6..5fde8f1ff 100644 --- a/rtsim/station.v +++ b/rtsim/station.v @@ -57,6 +57,7 @@ module station( // Local Bus for simulator configuration `AUTOMATIC_self ); +`undef AUTOMATIC_self `AUTOMATIC_decode @@ -128,4 +129,5 @@ adc_em #(.del(1)) a_for // auto adc_em #(.del(1)) a_rfl // auto (.clk(clk), .strobe(iq), .in(reflect), .rnd(rndb[12: 0]), .adc(a_reflect), `AUTOMATIC_a_rfl); +`undef AUTOMATIC_prng endmodule diff --git a/selfclean.sh b/selfclean.sh index 6dbc08299..642accd82 100644 --- a/selfclean.sh +++ b/selfclean.sh @@ -11,6 +11,7 @@ make -C dsp clean make -C dsp/feedforward clean make -C homeless/freq_demo clean make -C homeless clean +make -C projects/common/leep clean make -C localbus clean make -C build-tools/make-demo spotless make -C projects/test_marble_family/pps_lock clean @@ -26,3 +27,4 @@ make -C serial_io/chitchat clean make -C serial_io/EVG_EVR clean make -C soc/picorv32/test clean make -C fpga_family/xilinx clean +rm -r $(find * -name "__pycache__") diff --git a/selftest.sh b/selftest.sh index c0a850457..fc0aa4f3d 100644 --- a/selftest.sh +++ b/selftest.sh @@ -39,7 +39,7 @@ yosys -V echo 'puts "tclsh [info patchlevel]"' | tclsh flake8 --version if [ "$1" = "more" ]; then -python3 -c 'import nmigen; print("nmigen found")' +python3 -c 'import nmigen; print("nmigen found", nmigen.__version__)' riscv64-unknown-elf-gcc --version fi @@ -89,7 +89,10 @@ make -C homeless all checks make -C homeless/freq_demo ## leep_test -(cd projects/common && python3 -m unittest -v) +(cd projects/common && PYTHONPATH=../../build-tools python3 -m unittest -v) + +## leep_test2 +make -C projects/common/leep ## localbus make -C localbus diff --git a/serial_io/EVG_EVR/NMEA.c b/serial_io/EVG_EVR/NMEA.c index 5d0ad9da0..0a1a6f788 100644 --- a/serial_io/EVG_EVR/NMEA.c +++ b/serial_io/EVG_EVR/NMEA.c @@ -34,7 +34,7 @@ static int satellitesInView = -1; /* * Returns number of days since civil 1970-01-01 - * Ref: http://howardhinnant.github.io/date_algorithms.html#days_from_civil + * Ref: https://howardhinnant.github.io/date_algorithms.html#days_from_civil */ static int days_from_civil(int y, unsigned m, unsigned d) diff --git a/dsp/TinyEVG.html b/serial_io/EVG_EVR/TinyEVG.html similarity index 100% rename from dsp/TinyEVG.html rename to serial_io/EVG_EVR/TinyEVG.html diff --git a/serial_io/chitchat/chitchat_txrx_wrap_tb.v b/serial_io/chitchat/chitchat_txrx_wrap_tb.v index 9466140df..58f499f26 100644 --- a/serial_io/chitchat/chitchat_txrx_wrap_tb.v +++ b/serial_io/chitchat/chitchat_txrx_wrap_tb.v @@ -113,8 +113,8 @@ module chitchat_txrx_wrap_tb; if (tx_transmit_en) val_cnt <= val_cnt + 1; end - assign tx_valid0 = (val_cnt!=0 & (val_cnt % valid_period)==0); - assign tx_valid1 = (val_cnt!=0 & (val_cnt % valid_period)==5); + assign tx_valid0 = (val_cnt!=0) & ((val_cnt % valid_period)==0) & tx_transmit_en; + assign tx_valid1 = (val_cnt!=0) & ((val_cnt % valid_period)==5) & tx_transmit_en; reg [7:0] tx_data=0; diff --git a/serial_io/crc16.v b/serial_io/crc16.v index cd5ef4156..75a7d2ebc 100644 --- a/serial_io/crc16.v +++ b/serial_io/crc16.v @@ -13,7 +13,7 @@ module crc16( // D is the 16-bit input data (msb-first) // crc and O are the new and old 16-bit CRC // Generating polynomial is 0x1021 (normal form, leading 1 suppressed) - // Reference: http://en.wikipedia.org/wiki/Cyclic_redundancy_check + // Reference: https://en.wikipedia.org/wiki/Cyclic_redundancy_check crc[0] <= D[0]^O[0]^D[4]^O[4]^D[8]^O[8]^D[11]^O[11]^D[12]^O[12]; crc[1] <= D[1]^O[1]^D[5]^O[5]^D[9]^O[9]^D[12]^O[12]^D[13]^O[13]; crc[2] <= D[2]^O[2]^D[6]^O[6]^D[10]^O[10]^D[13]^O[13]^D[14]^O[14]; diff --git a/serial_io/negotiate.v b/serial_io/negotiate.v index 2de8392d3..a5a99a321 100644 --- a/serial_io/negotiate.v +++ b/serial_io/negotiate.v @@ -263,7 +263,7 @@ module negotiate( // 16-bit Ethernet configuration register as documented in // Networking Protocol Fundamentals, by James Long - // and http://grouper.ieee.org/groups/802/3/z/public/presentations/nov1996/RTpcs8b_sum5.pdf + // and https://grouper.ieee.org/groups/802/3/z/public/presentations/nov1996/RTpcs8b_sum5.pdf reg FD=1; // Full Duplex capable wire HD=0; // Half Duplex capable wire PS1=0; // Pause 1 diff --git a/serial_io/patt_gen_tb.v b/serial_io/patt_gen_tb.v index 68b559095..63062c083 100644 --- a/serial_io/patt_gen_tb.v +++ b/serial_io/patt_gen_tb.v @@ -57,7 +57,7 @@ module patt_gen_tb; // ---------------------- // Generate stimulus // ---------------------- - wire [4:0] pgen_rate; + wire [4:0] pgen_rate_maybe, pgen_rate; wire pgen_test_mode; wire [2:0] pgen_inc_step; wire [15:0] pgen_usr_data; @@ -89,7 +89,9 @@ module patt_gen_tb; end end - assign {pgen_rate, pgen_test_mode, pgen_inc_step, pgen_usr_data} = rand_setup; + assign {pgen_rate_maybe, pgen_test_mode, pgen_inc_step, pgen_usr_data} = rand_setup; + // pgen_rate = 1 is invalid + assign pgen_rate = (pgen_rate_maybe == 1) ? 2 : pgen_rate_maybe; flag_xdomain i_flag_xdomain ( .clk1 (tx_clk), .flagin_clk1 (rx_valid), @@ -131,6 +133,12 @@ module patt_gen_tb; reg rx_valid_dly [DELAY_DATA-1:0]; reg [15:0] rx_data_dly [DELAY_DATA-1:0]; + // Initialize delay pipe so simulation doesn't start with a bunch of Xs + integer ix; + initial begin + for (ix=0; ix N = number of bytes to write to mem # receive: ... Receive data and write it to mem starting at _startup_adr @@ -39,12 +40,14 @@ start_bootloader: # Wait for start character li t0, 'g' # the start char. to wait for + li t4, 'q' # the abort char., also to wait for li t1, 0 # t1 = 0, timeout counter li t2, BOOTLOADER_DELAY# t2 max. timeout count wait_loop: bgeu t1, t2, finished # if( t1 > max. timeout ) goto finished addi t1, t1, 1 # t1++ lw t3, 0x08(gp) # Get character from UART into t3 + beq t3, t4, finished # skip the timeout bne t3, t0, wait_loop # check t3 != 'g' # When we are here, we have received 'g' and not timed out diff --git a/soc/picorv32/gateware/picorv32.v b/soc/picorv32/gateware/picorv32.v index 9206bf4a0..4b569ba5f 100644 --- a/soc/picorv32/gateware/picorv32.v +++ b/soc/picorv32/gateware/picorv32.v @@ -23,7 +23,7 @@ /* verilator lint_off CASEOVERLAP */ /* verilator lint_off CASEINCOMPLETE */ -`timescale 1 ns / 1 ps +`timescale 1 ns / 1 ns // `default_nettype none // `define DEBUGNETS // `define DEBUGREGS diff --git a/soc/picorv32/gateware/uart_fifo_pack.v b/soc/picorv32/gateware/uart_fifo_pack.v index eb942b3dc..e1922df63 100644 --- a/soc/picorv32/gateware/uart_fifo_pack.v +++ b/soc/picorv32/gateware/uart_fifo_pack.v @@ -137,7 +137,9 @@ always @(posedge clk) begin if (|mem_wstrb && (mem_short_addr==UART_BAUDRATE)) begin if (mem_wstrb[0]) uprescale[ 7:0] <= mem_wdata[ 7:0]; if (mem_wstrb[1]) uprescale[15:8] <= mem_wdata[15:8]; +`ifndef YOSYS $display("new UART prescale value = 0x%04x", mem_wdata); +`endif end // ------------- // --- Reads --- diff --git a/soc/picorv32/project/cmod_a7/common/test.c b/soc/picorv32/project/cmod_a7/common/test.c index 0db3542e5..1623218db 100644 --- a/soc/picorv32/project/cmod_a7/common/test.c +++ b/soc/picorv32/project/cmod_a7/common/test.c @@ -24,7 +24,7 @@ unsigned xorshift32(unsigned *state) int cmd_memtest(volatile unsigned *base, unsigned len, unsigned stride, unsigned cycles) { // adapted from: - // https://github.com/cliffordwolf/picorv32/blob/master/picosoc/firmware.c + // https://github.com/YosysHQ/picorv32/blob/master/picosoc/firmware.c unsigned state; volatile uint8_t *base_byte = (volatile uint8_t*)base; @@ -68,7 +68,7 @@ int cmd_memtest(volatile unsigned *base, unsigned len, unsigned stride, unsigned // A simple Sieve of Eratosthenes // copied from: -// https://github.com/cliffordwolf/picorv32/blob/master/firmware/sieve.c +// https://github.com/YosysHQ/picorv32/blob/master/firmware/sieve.c static unsigned bitmap[BITMAP_SIZE/32]; static unsigned hash; diff --git a/soc/picorv32/project/cmod_a7/synth/digilent_cmod_a7.cfg b/soc/picorv32/project/cmod_a7/synth/digilent_cmod_a7.cfg index ac4487b5f..2cf86438a 100644 --- a/soc/picorv32/project/cmod_a7/synth/digilent_cmod_a7.cfg +++ b/soc/picorv32/project/cmod_a7/synth/digilent_cmod_a7.cfg @@ -1,7 +1,7 @@ # # Digilent Cmod A7 Xilinx Artix-7 FPGA # -# https://store.digilentinc.com/cmod-a7-breadboardable-artix-7-fpga-module/ +# https://digilent.com/shop/cmod-a7-35t-breadboardable-artix-7-fpga-module/ # # iManufacturer 1 Digilent diff --git a/soc/picorv32/project/kc705/sim/i2c_model.v b/soc/picorv32/project/kc705/sim/i2c_model.v index 6a70ae279..a68c64cb2 100644 --- a/soc/picorv32/project/kc705/sim/i2c_model.v +++ b/soc/picorv32/project/kc705/sim/i2c_model.v @@ -1,4 +1,4 @@ -// http://www.fpga4fun.com/I2C_2.html +// https://www.fpga4fun.com/I2C_2.html module i2c_model #( parameter I2C_ADR = 7'h27 ) ( diff --git a/soc/picorv32/rules.mk b/soc/picorv32/rules.mk index 6ff8fad88..71ea876ae 100644 --- a/soc/picorv32/rules.mk +++ b/soc/picorv32/rules.mk @@ -19,7 +19,7 @@ CCSPECS = -specs=picolibc.specs CLFLAGS = -march=rv32imc -mabi=ilp32 -ffreestanding -DBLOCK_RAM_SIZE=$(BLOCK_RAM_SIZE) -nostartfiles $(CCSPECS) CFLAGS = -std=c99 -Os -Wall -Wextra -Wundef -Wstrict-prototypes $(CLFLAGS) LDFLAGS = $(CLFLAGS) -Wl,--strip-debug,--print-memory-usage,-Bstatic,-Map,$*.map,--defsym,BLOCK_RAM_SIZE=$(BLOCK_RAM_SIZE),--gc-sections,--no-relax -T$(filter %.lds, $^) -# --no-relax is a workaround for https://github.com/riscv/riscv-binutils-gdb/issues/144 +# --no-relax is a workaround for https://github.com/riscvarchive/riscv-binutils-gdb/issues/144 # --verbose=3,-M for verbose linker output / debugging %.lst: %.elf @@ -41,8 +41,7 @@ LDFLAGS = $(CLFLAGS) -Wl,--strip-debug,--print-memory-usage,-Bstatic,-Map,$*.map %_load: %32.hex $(PYTHON) $(COMMON_DIR)/boot_load.py $< $(BOOTLOADER_SERIAL) --baud_rate $(BOOTLOADER_BAUDRATE) -# All testbenches use $stop, eliminating the `awk` dependency -%_check: %_tb +# All testbenches use $stop, eliminating the old `awk` dependency %_check: %_tb $(VERILOG_SIM) @@ -57,10 +56,12 @@ LDFLAGS = $(CLFLAGS) -Wl,--strip-debug,--print-memory-usage,-Bstatic,-Map,$*.map chmod -x $@ %_synth.bit: %.v - vivado -nojou -mode batch -source $(filter %.tcl, $^) -tclargs $(basename $@) $(BLOCK_RAM_SIZE) $(filter %.v, $^) + $(VIVADO_CMD) -source $(filter %.tcl, $^) -tclargs $(basename $@) $(BLOCK_RAM_SIZE) $(filter %.v, $^) -%_config: - xc3sprog -c jtaghs1_fast $(patsubst %_config,%.bit,$@) +# No serial number is provided in this rule, so it's only useful when +# a single FTDI device is plugged into your workstation +%_config: %.bit + xc3sprog -c jtaghs1_fast $< CLEAN += $(TARGET).vcd $(TARGET)_tb $(TARGET).map $(TARGET).lst $(TARGET).elf pico.trace CLEAN += $(TARGET)8.hex $(TARGET)32.hex $(TARGET)32.dat $(TARGET).o $(OBJS) diff --git a/soc/picorv32/test/badger_lwip/Makefile b/soc/picorv32/test/badger_lwip/Makefile index 7d5c5ccee..2772ca907 100644 --- a/soc/picorv32/test/badger_lwip/Makefile +++ b/soc/picorv32/test/badger_lwip/Makefile @@ -51,7 +51,7 @@ CFLAGS += -DLWIP_DEBUG # builds an hardware emulator for running / debugging lwip # -DETHERNET_MODEL_DEBUG V$(TARGET): $(SRC_V) $(TARGET)_sim.cpp ethernet_model.c tap_alloc.c crc32.c - verilator --trace -cc --exe --top-module $(TARGET)_tb \ + $(VERILATOR) --trace -cc --exe --top-module $(TARGET)_tb \ -Wno-PINMISSING -Wno-WIDTH -Wno-CASEINCOMPLETE \ -DBLOCK_RAM_SIZE=$(BLOCK_RAM_SIZE) \ -CFLAGS "-I$(BADGER_DIR)/tests" \ diff --git a/soc/picorv32/test/badger_lwip/README.md b/soc/picorv32/test/badger_lwip/README.md index b8686c7cf..5260a2a32 100644 --- a/soc/picorv32/test/badger_lwip/README.md +++ b/soc/picorv32/test/badger_lwip/README.md @@ -34,7 +34,7 @@ sudo ip link set dev tap0 up * to demonstrate DHCP, uncomment `dhcp_start(&netif);` and run a DHCP server on the local TAP0 network -### (Bridging)[https://wiki.archlinux.org/index.php/Network_bridge] tap0 +### (Bridging)[https://wiki.archlinux.org/title/Network_bridge] tap0 A bridge is like a virtual network switch, connecting two or more links together (here tap0 and eth0). This allows the Verilog simulation to access the Internet. ```bash diff --git a/soc/picorv32/test/bootLoad/bootLoad_tb.v b/soc/picorv32/test/bootLoad/bootLoad_tb.v index 9f2dfc9b2..29a45531f 100644 --- a/soc/picorv32/test/bootLoad/bootLoad_tb.v +++ b/soc/picorv32/test/bootLoad/bootLoad_tb.v @@ -122,6 +122,8 @@ module bootloader_tb; reg [31:0] _startup_adr_arr[0:0]; wire [31:0] _startup_adr = _startup_adr_arr[0]; reg [ 7:0] hexData [0:255]; + reg [1:0] jx; + integer start_time, delay, dok; //-------------------- // The test sequence @@ -168,26 +170,37 @@ module bootloader_tb; uart_read_task("!"); uart_read_task("!"); //------------------- - // Test timeout + // Test timeout or timeout bypass //------------------- + for (jx=0; jx<2; jx=jx+1) begin $display("\n"); @ (posedge mem_clk); reset <= 1; repeat (10) @(posedge mem_clk); reset <= 0; $display("---------------------"); - $display(" Testing timeout"); + $display(" Testing timeout %d", jx); $display("---------------------"); // Sync sequence: ok uart_read_task("o"); uart_read_task("o"); uart_read_task("k"); uart_read_task("\n"); + // exercise new (Aug 2024) feature of being able to bypass timeout + if (jx==1) uart_write_task("q"); + start_time = $time; // The user program should start talking after the timeout uart_read_task("!"); + delay = $time - start_time; + // Be very lenient, to avoid failure with past or future gcc versions + if (jx == 0) dok = (delay > 20000 && delay < 90000); + if (jx == 1) dok = (delay < 4000); + $display("\nmeasured delay %d ticks %s", delay, dok ? " OK" : "BAD"); + pass &= dok; uart_read_task("!"); uart_read_task("!"); $write("\n"); + end #500 if (pass) begin $display("PASS"); diff --git a/soc/picorv32/test/bootLoad/settings.h b/soc/picorv32/test/bootLoad/settings.h index 2ddda78fe..ce9aad1b2 100644 --- a/soc/picorv32/test/bootLoad/settings.h +++ b/soc/picorv32/test/bootLoad/settings.h @@ -13,7 +13,7 @@ #define F_CLK 100000000 // [Hz] for CMODA7 -#define BOOTLOADER_DELAY 16 +#define BOOTLOADER_DELAY 160 #define BOOTLOADER_BAUDRATE 9216000 // Used for fast simulation #endif diff --git a/soc/picorv32/test/memio/spiflash.v b/soc/picorv32/test/memio/spiflash.v index 298219d96..b9b69b511 100644 --- a/soc/picorv32/test/memio/spiflash.v +++ b/soc/picorv32/test/memio/spiflash.v @@ -17,7 +17,7 @@ * */ -`timescale 1 ns / 1 ps +`timescale 1 ns / 1 ns // // Simple SPI flash simulation model diff --git a/soc/picorv32/test/wfm/wfm_tb.v b/soc/picorv32/test/wfm/wfm_tb.v index ee2daedd8..0cb4d73de 100644 --- a/soc/picorv32/test/wfm/wfm_tb.v +++ b/soc/picorv32/test/wfm/wfm_tb.v @@ -93,7 +93,7 @@ module wfm_tb; // N_CH=2, CH1: 16'hdead, CH0: 16'beaf reg [31:0] adc_out_data= {16'hdead, 16'hbeaf}; always @(posedge dsp_clk) begin - adc_out_data <= ~adc_out_data; + if (dut.counting) adc_out_data <= ~adc_out_data; end // -------------------------------------------------------------- // wfm_pack module @@ -123,8 +123,8 @@ module wfm_tb; // Read CH1 and check data // wfm.c: SET_REG8(config_addr + WFM_CFG_BYTE_CHAN_SEL, 1); always @(posedge mem_clk) if (mem_read_stb) begin - if (v_addr % 2 == 0) pass &= (v_rdata == ~16'hdead); - if (v_addr % 2 == 1) pass &= (v_rdata == 16'hdead); + if (v_addr % 2 == 0) pass &= (v_rdata == 16'hdead); + if (v_addr % 2 == 1) pass &= (v_rdata == ~16'hdead); $display("Time: %g ns: addr: 0x%x, data : 0x%x %s\n", $time, v_addr, v_rdata, pass ? "PASS":"FAIL"); end endmodule diff --git a/tox.ini b/tox.ini index 0a22e8f3b..9e5c72c4e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,3 +1,3 @@ [flake8] -ignore=E402,E226,W503,W504,E124,E128,E221,E241,E251 +ignore=E402,E226,W504,E221,E241,E251 max-line-length=120