Implement generic MAX2771 raw Ethernet recorder
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
.PHONY: sim build program
|
||||
|
||||
sim:
|
||||
../../scripts/run_common_sim.sh
|
||||
|
||||
build:
|
||||
vivado -mode batch -source scripts/build_vivado.tcl
|
||||
|
||||
program:
|
||||
vivado -mode batch -source scripts/program_fpga.tcl
|
||||
@@ -0,0 +1,104 @@
|
||||
# Digilent Genesys 2 target
|
||||
|
||||
This directory contains only the Genesys 2 adaptation of the MAX2771 recorder.
|
||||
The protocol/capture/FIFO/MAC RTL comes from `../../rtl/common`; no portable
|
||||
module is copied here.
|
||||
|
||||
## Target-specific files
|
||||
|
||||
| File | Function |
|
||||
|---|---|
|
||||
| `rtl/max2771_recorder_top.sv` | connects common blocks, status counters and LEDs |
|
||||
| `rtl/ethernet_clock_gen_xilinx.sv` | 200 MHz differential input and primitive MMCM for 125 MHz/125 MHz +90° |
|
||||
| `rtl/rgmii_tx_xilinx.sv` | Kintex-7 ODDR implementation for TXD/TXCTL/TXC |
|
||||
| `rtl/phy_init_rtl8211e.sv` | polls Genesys RTL8211E PHY address 1 and gates 1000/full link |
|
||||
| `constraints/genesys2_max2771_recorder.xdc` | verified clock, JA, Ethernet and LED pins |
|
||||
| `scripts/build_vivado.tcl` | in-memory/non-project synthesis, P&R, reports and bitstream |
|
||||
| `scripts/program_fpga.tcl` | volatile JTAG configuration |
|
||||
|
||||
## Wiring
|
||||
|
||||
| Signal | FPGA ball | JA pin | Standard |
|
||||
|---|---:|---:|---|
|
||||
| `rf_clk_pmod` | U27 | JA[0] | LVCMOS33 |
|
||||
| `rf_data_pmod[0]` | U28 | JA[1] | LVCMOS33 |
|
||||
| `rf_data_pmod[1]` | T26 | JA[2] | LVCMOS33 |
|
||||
| `rf_data_pmod[2]` | T27 | JA[3] | LVCMOS33 |
|
||||
| `rf_data_pmod[3]` | T22 | JA[4] | LVCMOS33 |
|
||||
|
||||
Connect a common ground and use only a 3.3-V-compatible digital source.
|
||||
|
||||
## Simulation
|
||||
|
||||
From the repository root:
|
||||
|
||||
```bash
|
||||
make genesys2-sim
|
||||
```
|
||||
|
||||
This runs the common FIFO/protocol/MAC regression. Xilinx primitives are
|
||||
guarded by `SYNTHESIS`, so vendor simulation libraries are not required for the
|
||||
common regression.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
make genesys2-build
|
||||
```
|
||||
|
||||
Equivalent direct command:
|
||||
|
||||
```bash
|
||||
vivado -mode batch -source targets/genesys2/scripts/build_vivado.tcl
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```text
|
||||
build/genesys2/vivado/max2771_recorder.bit
|
||||
build/genesys2/reports/
|
||||
```
|
||||
|
||||
The build uses part `xc7k325tffg900-2`, top `max2771_recorder_top`, creates an
|
||||
in-memory project, fails on negative setup/hold slack or unexpected major CDC
|
||||
warnings and uses no block design or generated IP. A clean Vivado 2026.1 build
|
||||
on 1 August 2026 completed synthesis, placement, routing, DRC and bitstream
|
||||
generation with +0.212 ns worst setup slack and +0.090 ns worst hold slack.
|
||||
|
||||
## Programming
|
||||
|
||||
Connect the Digilent JTAG USB port and run:
|
||||
|
||||
```bash
|
||||
make genesys2-program
|
||||
```
|
||||
|
||||
This configures the FPGA volatile SRAM. It does not write configuration flash.
|
||||
Power cycling removes the design.
|
||||
|
||||
## Clocking and RGMII
|
||||
|
||||
The 200 MHz differential oscillator drives an MMCM with a 1000 MHz VCO. It
|
||||
generates 125 MHz at 0° for the MAC/data ODDRs and 125 MHz at +90° for the TXC
|
||||
ODDR. The resulting nominal +2 ns clock shift is required because the board
|
||||
straps RTL8211E transmit delay off.
|
||||
|
||||
PHY reset remains asserted for 10 ms, register access waits another 30 ms, and
|
||||
transmit starts only after PHY-specific status reports resolved 1000 Mb/s/full
|
||||
duplex link. Ethernet receive is not implemented.
|
||||
|
||||
## LEDs
|
||||
|
||||
| LED | Meaning |
|
||||
|---:|---|
|
||||
| 0 | MMCM locked/system ready |
|
||||
| 1 | qualified 1000/full link |
|
||||
| 2 | RF sample activity |
|
||||
| 3 | FIFO non-empty |
|
||||
| 4 | sample loss/overflow latched |
|
||||
| 5 | frame activity |
|
||||
| 6 | first PHY status read completed |
|
||||
| 7 | Ethernet-domain heartbeat |
|
||||
|
||||
See [hardware assumptions](docs/hardware_assumptions.md) and
|
||||
[timing constraints](docs/timing_constraints.md) before hardware sign-off.
|
||||
@@ -0,0 +1,75 @@
|
||||
###############################################################################
|
||||
# Digilent Genesys 2 / MAX2771 raw recorder
|
||||
# Board pins copied from Digilent Genesys-2-Master.xdc, repository commit
|
||||
# 00a3404901f35aa9567b01ecb3f2c233b6efe9f4. FPGA: xc7k325tffg900-2.
|
||||
###############################################################################
|
||||
|
||||
# 200 MHz differential system oscillator.
|
||||
set_property -dict {PACKAGE_PIN AD12 IOSTANDARD LVDS} [get_ports sysclk_p]
|
||||
set_property -dict {PACKAGE_PIN AD11 IOSTANDARD LVDS} [get_ports sysclk_n]
|
||||
create_clock -name sys_clk_200 -period 5.000 [get_ports sysclk_p]
|
||||
|
||||
# Active-low CPU reset button.
|
||||
set_property -dict {PACKAGE_PIN R19 IOSTANDARD LVCMOS33 PULLUP TRUE} [get_ports cpu_resetn]
|
||||
|
||||
# MAX2771 on JA. This mapping is supplied by the design specification and also
|
||||
# matches the official Digilent JA pin locations.
|
||||
set_property -dict {PACKAGE_PIN U27 IOSTANDARD LVCMOS33} [get_ports rf_clk_pmod]
|
||||
set_property -dict {PACKAGE_PIN U28 IOSTANDARD LVCMOS33} [get_ports {rf_data_pmod[0]}]
|
||||
set_property -dict {PACKAGE_PIN T26 IOSTANDARD LVCMOS33} [get_ports {rf_data_pmod[1]}]
|
||||
set_property -dict {PACKAGE_PIN T27 IOSTANDARD LVCMOS33} [get_ports {rf_data_pmod[2]}]
|
||||
set_property -dict {PACKAGE_PIN T22 IOSTANDARD LVCMOS33} [get_ports {rf_data_pmod[3]}]
|
||||
create_clock -name rf_sample_clk -period 30.547 -waveform {0.000 15.2735} [get_ports rf_clk_pmod]
|
||||
|
||||
# MAX2771 clock-to-data timing is not supplied by the board manual or by the
|
||||
# project specification. Do not invent it. Before hardware sign-off, replace
|
||||
# RF_INPUT_MAX_NS and RF_INPUT_MIN_NS below with limits derived from the selected
|
||||
# MAX2771 output mode plus measured cable/PMOD skew, then uncomment the commands.
|
||||
# set_input_delay -clock rf_sample_clk -max RF_INPUT_MAX_NS [get_ports {rf_data_pmod[*]}]
|
||||
# set_input_delay -clock rf_sample_clk -min RF_INPUT_MIN_NS [get_ports {rf_data_pmod[*]}]
|
||||
|
||||
# Realtek RTL8211E-VL management and transmit-side RGMII, Bank 33 at 1.5 V.
|
||||
set_property -dict {PACKAGE_PIN AF12 IOSTANDARD LVCMOS15 SLEW SLOW} [get_ports eth_mdc]
|
||||
set_property -dict {PACKAGE_PIN AG12 IOSTANDARD LVCMOS15 PULLUP TRUE} [get_ports eth_mdio]
|
||||
set_property -dict {PACKAGE_PIN AH24 IOSTANDARD LVCMOS33 SLEW SLOW} [get_ports eth_phyrst_n]
|
||||
set_property -dict {PACKAGE_PIN AE10 IOSTANDARD LVCMOS15 SLEW FAST} [get_ports eth_txck]
|
||||
set_property -dict {PACKAGE_PIN AJ12 IOSTANDARD LVCMOS15 SLEW FAST} [get_ports {eth_txd[0]}]
|
||||
set_property -dict {PACKAGE_PIN AK11 IOSTANDARD LVCMOS15 SLEW FAST} [get_ports {eth_txd[1]}]
|
||||
set_property -dict {PACKAGE_PIN AJ11 IOSTANDARD LVCMOS15 SLEW FAST} [get_ports {eth_txd[2]}]
|
||||
set_property -dict {PACKAGE_PIN AK10 IOSTANDARD LVCMOS15 SLEW FAST} [get_ports {eth_txd[3]}]
|
||||
set_property -dict {PACKAGE_PIN AK14 IOSTANDARD LVCMOS15 SLEW FAST} [get_ports eth_txctl]
|
||||
|
||||
# The MMCM VCO is 1000 MHz (200 MHz * 5). CLKOUT0 and CLKOUT1 are 125 MHz;
|
||||
# CLKOUT1 has +90 degrees = +2.000 ns. Vivado derives internal MMCM clocks.
|
||||
# This explicit forwarded-clock definition constrains the PHY interface.
|
||||
create_generated_clock -name rgmii_txc \
|
||||
-source [get_pins u_clock_gen/u_mmcm/CLKOUT1] -divide_by 1 [get_ports eth_txck]
|
||||
create_generated_clock -name mdio_mdc \
|
||||
-source [get_pins u_clock_gen/u_mmcm/CLKOUT0] -divide_by 50 [get_ports eth_mdc]
|
||||
|
||||
# RGMII v2.0 source-synchronous budget: the receiver requires 1.0 ns setup and
|
||||
# 1.0 ns hold around the delayed TXC edge. The +2 ns FPGA clock phase centers the
|
||||
# data eye because the board straps RTL8211E TXDLY off.
|
||||
set_output_delay -clock rgmii_txc -max 1.000 [get_ports {eth_txd[*] eth_txctl}]
|
||||
set_output_delay -clock rgmii_txc -min -1.000 [get_ports {eth_txd[*] eth_txctl}]
|
||||
|
||||
# All RF<->Ethernet crossings are structurally confined to u_sample_fifo Gray
|
||||
# pointers, u_drop_sync Gray counter, u_sample_pulse_sync, and reset synchronizers.
|
||||
# There is intentionally no ordinary logic path between these asynchronous clocks.
|
||||
set_clock_groups -asynchronous \
|
||||
-group [get_clocks rf_sample_clk] \
|
||||
-group [get_clocks -include_generated_clocks sys_clk_200]
|
||||
|
||||
# Force the four MAX2771 sampling registers and all RGMII DDR registers into IOBs.
|
||||
set_property IOB TRUE [get_cells -hier -filter {NAME =~ *u_capture/sample_iob_reg*}]
|
||||
set_property IOB TRUE [get_cells -hier -filter {NAME =~ *u_rgmii_tx*g_txd_oddr*u_oddr* || NAME =~ *u_rgmii_tx*u_ctl_oddr* || NAME =~ *u_rgmii_tx*u_txc_oddr*}]
|
||||
|
||||
# Eight verified user LEDs.
|
||||
set_property -dict {PACKAGE_PIN T28 IOSTANDARD LVCMOS33} [get_ports {led[0]}]
|
||||
set_property -dict {PACKAGE_PIN V19 IOSTANDARD LVCMOS33} [get_ports {led[1]}]
|
||||
set_property -dict {PACKAGE_PIN U30 IOSTANDARD LVCMOS33} [get_ports {led[2]}]
|
||||
set_property -dict {PACKAGE_PIN U29 IOSTANDARD LVCMOS33} [get_ports {led[3]}]
|
||||
set_property -dict {PACKAGE_PIN V20 IOSTANDARD LVCMOS33} [get_ports {led[4]}]
|
||||
set_property -dict {PACKAGE_PIN V26 IOSTANDARD LVCMOS33} [get_ports {led[5]}]
|
||||
set_property -dict {PACKAGE_PIN W24 IOSTANDARD LVCMOS33} [get_ports {led[6]}]
|
||||
set_property -dict {PACKAGE_PIN W23 IOSTANDARD LVCMOS33} [get_ports {led[7]}]
|
||||
@@ -0,0 +1,48 @@
|
||||
# Hardware information and assumptions
|
||||
|
||||
## Verified board information
|
||||
|
||||
The pin source is Digilent's official [`Genesys-2-Master.xdc`](https://github.com/Digilent/digilent-xdc/blob/00a3404901f35aa9567b01ecb3f2c233b6efe9f4/Genesys-2-Master.xdc), commit `00a3404901f35aa9567b01ecb3f2c233b6efe9f4`. The official board definition names part `xc7k325tffg900-2` and the system oscillator as 200 MHz. The [Genesys 2 reference manual](https://digilent.com/reference/_media/reference/programmable-logic/genesys-2/genesys2_rm.pdf) identifies the PHY as RTL8211E-VL.
|
||||
|
||||
| Function | FPGA pin | Standard | Notes |
|
||||
|---|---|---|---|
|
||||
| `sysclk_p/n` | AD12 / AD11 | LVDS | 200 MHz differential oscillator |
|
||||
| `cpu_resetn` | R19 | LVCMOS33 | active-low board reset |
|
||||
| MDC / MDIO | AF12 / AG12 | LVCMOS15 | PHY address strapped to 1 |
|
||||
| RGMII TXC | AE10 | LVCMOS15 | Bank 33, 1.5 V |
|
||||
| RGMII TXD[0:3] | AJ12, AK11, AJ11, AK10 | LVCMOS15 | in vector order |
|
||||
| RGMII TXCTL | AK14 | LVCMOS15 | no TX error is generated |
|
||||
| PHY reset | AH24 | LVCMOS33 | active low |
|
||||
| PHY INTB / PMEB | AK16 / AK15 | LVCMOS18 | open-drain; not used by this design |
|
||||
| RGMII RXC | AG10 | LVCMOS15 | receive path not implemented |
|
||||
| RGMII RXCTL | AH11 | LVCMOS15 | receive path not implemented |
|
||||
| RGMII RXD[0:3] | AJ14, AH14, AK13, AJ13 | LVCMOS15 | receive path not implemented |
|
||||
| LEDs[0:7] | T28, V19, U30, U29, V20, V26, W24, W23 | LVCMOS33 | user status LEDs |
|
||||
|
||||
The reference manual states that the board straps auto-negotiation on, advertises 10/100/1000 modes, selects PHY address `00001`, and selects no PHY-internal RGMII RX or TX delay. It also shows a 25 MHz PHY crystal; the FPGA does not supply the PHY reference clock.
|
||||
|
||||
The RTL8211E datasheet requires PHYRSTB low for at least 10 ms and a further 30 ms before register access. The RTL implements both intervals. Link gating polls PHY-specific status register `0x11`; transmit is enabled only when bits `[15:14]=10` (1000 Mb/s), bit 13 (full duplex), bit 11 (resolved), and bit 10 (real-time link) are all set.
|
||||
|
||||
## Assumptions from the specification
|
||||
|
||||
- `rf_clk_pmod` is nominally 32.736 MHz.
|
||||
- A four-bit word is valid on every configured sampling edge.
|
||||
- Default capture is rising-edge and default packing is earlier-sample-high-nibble.
|
||||
- JA is a 3.3 V interface and the MAX2771-side signals presented to JA are electrically compatible with LVCMOS33.
|
||||
- Ethernet destination is broadcast and source is the fixed locally administered address `02:27:71:00:00:01`.
|
||||
|
||||
## MAX2771 datasheet confirmation required
|
||||
|
||||
- Confirm the selected MAX2771 digital-output mode really produces one complete four-bit word per RF clock edge.
|
||||
- Confirm output voltage, drive strength, clock polarity, setup, hold, and clock-to-output limits for the exact supply and load.
|
||||
- Confirm whether the default rising capture edge is the center of the valid data eye. Change `CAPTURE_RISING` only after that check.
|
||||
- Confirm whether the four bits represent the desired channel/order. RTL records `[3:0]` literally and deliberately does not reinterpret it.
|
||||
|
||||
## Measurement required
|
||||
|
||||
- Measure MAX2771 clock-to-data timing at the Genesys 2 JA pins, including cable/adapter skew. Put the resulting min/max values into the commented `set_input_delay` commands in the XDC.
|
||||
- Measure the four data-line skews if wiring is not length matched.
|
||||
- Verify the forwarded RGMII TX clock is centered approximately 2.0 ns after TXD/TXCTL transitions at the PHY pins. The MMCM provides the nominal shift; board trace and I/O delay still deserve hardware measurement.
|
||||
- Confirm clean 3.3 V levels, ground return, edge quality, and absence of overshoot at JA before long captures.
|
||||
|
||||
No unverified board pin has been inserted into the constraints.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Timing constraints and closure
|
||||
|
||||
## Clock plan
|
||||
|
||||
| Clock | Source | Frequency / period | Use |
|
||||
|---|---|---|---|
|
||||
| `sys_clk_200` | AD12/AD11 LVDS oscillator | 200 MHz / 5.000 ns | MMCM input |
|
||||
| `clk125_i` | MMCM VCO 1000 MHz, divide 8 | 125 MHz / 8.000 ns, 0° | packetizer, MAC, MDIO, RGMII data ODDR |
|
||||
| `clk125_90_i` | same MMCM output divide | 125 MHz / 8.000 ns, +90° | forwarded RGMII TXC ODDR |
|
||||
| `rgmii_txc` | ODDR from `clk125_90_i` | 125 MHz, +2.000 ns | PHY source-synchronous clock |
|
||||
| `mdio_mdc` | counter from 125 MHz | 2.5 MHz | Clause-22 management |
|
||||
| `rf_sample_clk` | U27 | 32.736 MHz / 30.547 ns | MAX2771 capture and FIFO write |
|
||||
|
||||
The RTL8211E is strapped with TX delay disabled. Therefore the FPGA creates RGMII-ID source-clock skew: GMII/RGMII data changes from ODDRs clocked at 0°, and TXC changes from an ODDR clocked at +90°. At 125 MHz, 90° is 2.000 ns. RGMII TX output delays use the standard 1.0 ns setup/1.0 ns hold receive budget around that centered clock edge.
|
||||
|
||||
## CDC exceptions
|
||||
|
||||
The RF and Ethernet clocks are asynchronous. The only crossings are:
|
||||
|
||||
- FIFO write/read Gray pointers, each through two `ASYNC_REG` stages;
|
||||
- the Gray-coded cumulative drop counter, through two `ASYNC_REG` stages;
|
||||
- a toggle-based sample-activity indicator, through two `ASYNC_REG` stages;
|
||||
- reset synchronizers with asynchronous assertion and synchronous deassertion.
|
||||
|
||||
The XDC asynchronous clock grouping is valid because no ordinary data/control logic crosses those groups. The multi-bit FIFO data is stored in true dual-clock block RAM and is read only under synchronized pointer control; it is not sampled as a combinational bus crossing.
|
||||
|
||||
Vivado reports CDC-6 for the three intentionally Gray-coded buses even when it recognizes their two ASYNC_REG stages. The build script accepts only named FIFO/drop-counter instances and fails for any other Warning or Critical CDC object.
|
||||
|
||||
## MAX2771 input delay boundary
|
||||
|
||||
The clock is constrained to 30.547 ns and the input registers are forced into IOBs. The project does **not** claim RF input setup/hold sign-off yet: neither the task input nor the Genesys 2 documentation provides MAX2771 `tCO`, cable delay, or inter-bit skew. The XDC contains conspicuous placeholder commands rather than fabricated numbers.
|
||||
|
||||
Before hardware sign-off:
|
||||
|
||||
1. derive earliest/latest data arrival at each FPGA pin from the MAX2771 datasheet;
|
||||
2. add measured cable/adapter and bit-to-bit skew;
|
||||
3. replace the placeholder max/min values in the XDC;
|
||||
4. rerun `make genesys2-build` and inspect
|
||||
`build/genesys2/reports/timing_post_route.rpt`;
|
||||
5. verify the eye with an oscilloscope or logic analyzer at JA.
|
||||
|
||||
The current 2026.1 build routes successfully with setup WNS `+0.212 ns` and hold WNS `+0.090 ns` for constrained paths. These numbers do not replace the missing external RF input-delay model.
|
||||
@@ -0,0 +1,54 @@
|
||||
`default_nettype none
|
||||
|
||||
// Genesys 2: 200 MHz differential oscillator -> 1000 MHz MMCM VCO ->
|
||||
// 125 MHz GMII clock and a +90 degree (2.000 ns) RGMII TX clock.
|
||||
module ethernet_clock_gen (
|
||||
input wire logic sysclk_p,
|
||||
input wire logic sysclk_n,
|
||||
input wire logic reset,
|
||||
output logic eth_clk,
|
||||
output logic eth_clk90,
|
||||
output logic locked
|
||||
);
|
||||
`ifdef SYNTHESIS
|
||||
wire logic clk200;
|
||||
wire logic clkfb;
|
||||
wire logic clkfb_buf;
|
||||
wire logic clk125_i;
|
||||
wire logic clk125_90_i;
|
||||
wire logic mmcm_locked;
|
||||
|
||||
IBUFDS #(.DIFF_TERM("FALSE"), .IBUF_LOW_PWR("TRUE")) u_sysclk_ibuf (
|
||||
.I(sysclk_p), .IB(sysclk_n), .O(clk200)
|
||||
);
|
||||
MMCME2_BASE #(
|
||||
.BANDWIDTH("OPTIMIZED"),
|
||||
.CLKIN1_PERIOD(5.000),
|
||||
.DIVCLK_DIVIDE(1),
|
||||
.CLKFBOUT_MULT_F(5.000),
|
||||
.CLKOUT0_DIVIDE_F(8.000),
|
||||
.CLKOUT0_PHASE(0.000),
|
||||
.CLKOUT1_DIVIDE(8),
|
||||
.CLKOUT1_PHASE(90.000),
|
||||
.STARTUP_WAIT("FALSE")
|
||||
) u_mmcm (
|
||||
.CLKIN1(clk200), .CLKFBIN(clkfb_buf), .RST(reset), .PWRDWN(1'b0),
|
||||
.CLKFBOUT(clkfb), .CLKOUT0(clk125_i), .CLKOUT1(clk125_90_i),
|
||||
.LOCKED(mmcm_locked),
|
||||
.CLKOUT0B(), .CLKOUT1B(), .CLKOUT2(), .CLKOUT2B(), .CLKOUT3(),
|
||||
.CLKOUT3B(), .CLKOUT4(), .CLKOUT5(), .CLKOUT6(), .CLKFBOUTB()
|
||||
);
|
||||
BUFG u_fb_buf (.I(clkfb), .O(clkfb_buf));
|
||||
BUFG u_clk_buf (.I(clk125_i), .O(eth_clk));
|
||||
BUFG u_clk90_buf (.I(clk125_90_i), .O(eth_clk90));
|
||||
assign locked = mmcm_locked;
|
||||
`else
|
||||
// Top-level behavioral simulation supplies a 125 MHz clock on sysclk_p.
|
||||
// Unit tests use GMII directly; this branch avoids a UNISIM dependency.
|
||||
assign eth_clk = sysclk_p;
|
||||
assign eth_clk90 = sysclk_p;
|
||||
assign locked = !reset;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,172 @@
|
||||
`default_nettype none
|
||||
|
||||
module max2771_recorder_top #(
|
||||
parameter bit CAPTURE_RISING = 1'b1,
|
||||
parameter bit EARLIER_SAMPLE_HIGH_NIBBLE = 1'b1,
|
||||
parameter int unsigned FIFO_DEPTH = 16384
|
||||
) (
|
||||
input wire logic sysclk_p,
|
||||
input wire logic sysclk_n,
|
||||
input wire logic cpu_resetn,
|
||||
input wire logic rf_clk_pmod,
|
||||
input wire logic [3:0] rf_data_pmod,
|
||||
|
||||
output logic eth_mdc,
|
||||
inout wire logic eth_mdio,
|
||||
output logic eth_phyrst_n,
|
||||
output logic eth_txck,
|
||||
output logic [3:0] eth_txd,
|
||||
output logic eth_txctl,
|
||||
output logic [7:0] led
|
||||
);
|
||||
localparam int unsigned FIFO_LEVEL_W = $clog2(FIFO_DEPTH) + 1;
|
||||
logic eth_clk, eth_clk90, mmcm_locked;
|
||||
logic eth_rst, rf_rst;
|
||||
logic rf_fifo_clk;
|
||||
logic fifo_w_en, fifo_full;
|
||||
logic [68:0] fifo_w_data;
|
||||
(* KEEP = "TRUE" *) logic [FIFO_LEVEL_W-1:0] fifo_w_level, fifo_r_level;
|
||||
(* KEEP = "TRUE" *) logic [63:0] samples_seen, samples_captured, samples_dropped;
|
||||
logic [63:0] dropped_samples_eth;
|
||||
logic loss_latched_rf;
|
||||
logic sample_activity_rf, sample_activity_eth;
|
||||
logic fifo_rd_en, fifo_rd_valid, fifo_empty;
|
||||
logic [68:0] fifo_rd_data;
|
||||
(* KEEP = "TRUE" *) logic [63:0] fifo_overflow_attempts, fifo_underflow_attempts;
|
||||
|
||||
logic phy_reset_complete;
|
||||
logic mdio_start, mdio_write, mdio_busy, mdio_done, mdio_read_valid;
|
||||
logic [4:0] mdio_phy_address, mdio_register_address;
|
||||
logic [15:0] mdio_write_data, mdio_read_data;
|
||||
logic mdio_i, mdio_o, mdio_oe;
|
||||
logic phy_init_complete, link_ready;
|
||||
(* KEEP = "TRUE" *) logic [63:0] link_down_events;
|
||||
|
||||
logic mac_frame_start, mac_payload_valid, mac_payload_ready, mac_busy, mac_frame_done;
|
||||
logic [10:0] mac_payload_length;
|
||||
logic [7:0] mac_payload_data;
|
||||
logic [7:0] gmii_txd;
|
||||
logic gmii_tx_en, gmii_tx_er;
|
||||
(* KEEP = "TRUE" *) logic [63:0] frames_transmitted, payload_bytes_transmitted;
|
||||
logic [31:0] packet_sequence;
|
||||
logic frame_activity_pulse;
|
||||
logic [23:0] sample_led_stretch, frame_led_stretch;
|
||||
logic [26:0] heartbeat;
|
||||
|
||||
ethernet_clock_gen u_clock_gen (
|
||||
.sysclk_p(sysclk_p), .sysclk_n(sysclk_n), .reset(!cpu_resetn),
|
||||
.eth_clk(eth_clk), .eth_clk90(eth_clk90), .locked(mmcm_locked)
|
||||
);
|
||||
reset_sync u_eth_reset (.clk(eth_clk), .arst(!cpu_resetn || !mmcm_locked), .srst(eth_rst));
|
||||
|
||||
max2771_capture #(.CAPTURE_RISING(CAPTURE_RISING)) u_capture (
|
||||
.rf_clk_pmod(rf_clk_pmod), .rst(rf_rst), .rf_data_pmod(rf_data_pmod),
|
||||
.fifo_full(fifo_full), .fifo_w_clk(rf_fifo_clk), .fifo_w_en(fifo_w_en),
|
||||
.fifo_w_data(fifo_w_data), .samples_seen(samples_seen),
|
||||
.samples_captured(samples_captured), .samples_dropped(samples_dropped),
|
||||
.loss_latched(loss_latched_rf), .sample_activity_pulse(sample_activity_rf)
|
||||
);
|
||||
reset_sync u_rf_reset (.clk(rf_fifo_clk), .arst(!cpu_resetn || !mmcm_locked), .srst(rf_rst));
|
||||
|
||||
async_fifo_gray #(.DATA_WIDTH(69), .DEPTH(FIFO_DEPTH)) u_sample_fifo (
|
||||
.w_clk(rf_fifo_clk), .w_rst(rf_rst), .w_en(fifo_w_en), .w_data(fifo_w_data),
|
||||
.w_full(fifo_full), .w_level(fifo_w_level),
|
||||
.overflow_attempt_count(fifo_overflow_attempts),
|
||||
.r_clk(eth_clk), .r_rst(eth_rst), .r_en(fifo_rd_en), .r_data(fifo_rd_data),
|
||||
.r_valid(fifo_rd_valid), .r_empty(fifo_empty), .r_level(fifo_r_level),
|
||||
.underflow_attempt_count(fifo_underflow_attempts)
|
||||
);
|
||||
|
||||
gray_counter_sync #(.WIDTH(64)) u_drop_sync (
|
||||
.src_clk(rf_fifo_clk), .src_rst(rf_rst), .src_binary(samples_dropped),
|
||||
.dst_clk(eth_clk), .dst_rst(eth_rst), .dst_binary(dropped_samples_eth)
|
||||
);
|
||||
pulse_sync u_sample_pulse_sync (
|
||||
.src_clk(rf_fifo_clk), .src_rst(rf_rst), .src_pulse(sample_activity_rf),
|
||||
.dst_clk(eth_clk), .dst_rst(eth_rst), .dst_pulse(sample_activity_eth)
|
||||
);
|
||||
|
||||
phy_reset_ctrl u_phy_reset (
|
||||
.clk(eth_clk), .rst(eth_rst), .phy_reset_n(eth_phyrst_n),
|
||||
.reset_complete(phy_reset_complete)
|
||||
);
|
||||
phy_init u_phy_init (
|
||||
.clk(eth_clk), .rst(eth_rst), .phy_reset_complete(phy_reset_complete),
|
||||
.mdio_start(mdio_start), .mdio_write(mdio_write),
|
||||
.mdio_phy_address(mdio_phy_address),
|
||||
.mdio_register_address(mdio_register_address), .mdio_write_data(mdio_write_data),
|
||||
.mdio_busy(mdio_busy), .mdio_done(mdio_done), .mdio_read_data(mdio_read_data),
|
||||
.initialization_complete(phy_init_complete), .link_ready(link_ready),
|
||||
.link_down_events(link_down_events)
|
||||
);
|
||||
mdio_master u_mdio (
|
||||
.clk(eth_clk), .rst(eth_rst), .start(mdio_start),
|
||||
.command_write(mdio_write), .phy_address(mdio_phy_address),
|
||||
.register_address(mdio_register_address), .write_data(mdio_write_data),
|
||||
.busy(mdio_busy), .done(mdio_done), .read_data(mdio_read_data),
|
||||
.read_valid(mdio_read_valid), .mdc(eth_mdc),
|
||||
.mdio_i(mdio_i), .mdio_o(mdio_o), .mdio_oe(mdio_oe)
|
||||
);
|
||||
`ifdef SYNTHESIS
|
||||
IOBUF u_mdio_iobuf (.I(mdio_o), .O(mdio_i), .T(!mdio_oe), .IO(eth_mdio));
|
||||
`else
|
||||
assign eth_mdio = mdio_oe ? mdio_o : 1'bz;
|
||||
assign mdio_i = eth_mdio;
|
||||
`endif
|
||||
|
||||
ethernet_packetizer #(
|
||||
.FIFO_DEPTH(FIFO_DEPTH), .FULL_SAMPLES(2936),
|
||||
.EARLIER_SAMPLE_HIGH_NIBBLE(EARLIER_SAMPLE_HIGH_NIBBLE)
|
||||
) u_packetizer (
|
||||
.clk(eth_clk), .rst(eth_rst), .link_ready(link_ready),
|
||||
.dropped_samples_low(dropped_samples_eth[31:0]),
|
||||
.fifo_empty(fifo_empty), .fifo_level(fifo_r_level),
|
||||
.fifo_rd_en(fifo_rd_en), .fifo_rd_data(fifo_rd_data),
|
||||
.fifo_rd_valid(fifo_rd_valid), .mac_frame_start(mac_frame_start),
|
||||
.mac_payload_length(mac_payload_length), .mac_payload_data(mac_payload_data),
|
||||
.mac_payload_valid(mac_payload_valid), .mac_payload_ready(mac_payload_ready),
|
||||
.mac_busy(mac_busy), .mac_frame_done(mac_frame_done),
|
||||
.packet_sequence(packet_sequence), .frame_activity_pulse(frame_activity_pulse)
|
||||
);
|
||||
ethernet_tx_mac u_mac (
|
||||
.clk(eth_clk), .rst(eth_rst), .frame_start(mac_frame_start),
|
||||
.payload_length(mac_payload_length), .payload_data(mac_payload_data),
|
||||
.payload_valid(mac_payload_valid), .payload_ready(mac_payload_ready),
|
||||
.gmii_txd(gmii_txd), .gmii_tx_en(gmii_tx_en), .gmii_tx_er(gmii_tx_er),
|
||||
.busy(mac_busy), .frame_done(mac_frame_done),
|
||||
.frames_transmitted(frames_transmitted),
|
||||
.payload_bytes_transmitted(payload_bytes_transmitted)
|
||||
);
|
||||
rgmii_tx u_rgmii_tx (
|
||||
.gmii_clk(eth_clk), .tx_clk90(eth_clk90), .gmii_txd(gmii_txd),
|
||||
.gmii_tx_en(gmii_tx_en), .gmii_tx_er(gmii_tx_er),
|
||||
.rgmii_txc(eth_txck), .rgmii_txd(eth_txd), .rgmii_tx_ctl(eth_txctl)
|
||||
);
|
||||
|
||||
always_ff @(posedge eth_clk) begin
|
||||
if (eth_rst) begin
|
||||
sample_led_stretch <= 24'd0;
|
||||
frame_led_stretch <= 24'd0;
|
||||
heartbeat <= 27'd0;
|
||||
end else begin
|
||||
heartbeat <= heartbeat + 27'd1;
|
||||
if (sample_activity_eth) sample_led_stretch <= 24'hffffff;
|
||||
else if (sample_led_stretch != 24'd0) sample_led_stretch <= sample_led_stretch - 24'd1;
|
||||
if (frame_activity_pulse) frame_led_stretch <= 24'hffffff;
|
||||
else if (frame_led_stretch != 24'd0) frame_led_stretch <= frame_led_stretch - 24'd1;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
led[0] = mmcm_locked && !eth_rst;
|
||||
led[1] = link_ready;
|
||||
led[2] = (sample_led_stretch != 24'd0);
|
||||
led[3] = !fifo_empty;
|
||||
led[4] = loss_latched_rf;
|
||||
led[5] = (frame_led_stretch != 24'd0);
|
||||
led[6] = phy_init_complete;
|
||||
led[7] = heartbeat[26];
|
||||
end
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,85 @@
|
||||
`default_nettype none
|
||||
|
||||
// The Genesys 2 straps the RTL8211E-VL for auto-negotiation and PHY address 1.
|
||||
// After the mandatory post-reset settling time, this controller polls PHYSR
|
||||
// (register 0x11) and permits transmit only for resolved 1000 Mb/s full duplex.
|
||||
module phy_init #(
|
||||
parameter int unsigned CLK_HZ = 125_000_000,
|
||||
parameter int unsigned SETTLE_MS = 30,
|
||||
parameter int unsigned POLL_MS = 100
|
||||
) (
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
input wire logic phy_reset_complete,
|
||||
output logic mdio_start,
|
||||
output logic mdio_write,
|
||||
output logic [4:0] mdio_phy_address,
|
||||
output logic [4:0] mdio_register_address,
|
||||
output logic [15:0] mdio_write_data,
|
||||
input wire logic mdio_busy,
|
||||
input wire logic mdio_done,
|
||||
input wire logic [15:0] mdio_read_data,
|
||||
output logic initialization_complete,
|
||||
output logic link_ready,
|
||||
output logic [63:0] link_down_events
|
||||
);
|
||||
localparam longint unsigned SETTLE_CYCLES = (CLK_HZ / 1000) * SETTLE_MS;
|
||||
localparam longint unsigned POLL_CYCLES = (CLK_HZ / 1000) * POLL_MS;
|
||||
localparam int unsigned TIMER_W = $clog2(((POLL_CYCLES > SETTLE_CYCLES) ? POLL_CYCLES : SETTLE_CYCLES) + 1);
|
||||
typedef enum logic [2:0] {I_WAIT_RESET, I_SETTLE, I_START, I_WAIT_MDIO, I_POLL_WAIT} istate_t;
|
||||
istate_t state;
|
||||
logic [TIMER_W-1:0] timer;
|
||||
logic new_link_ready;
|
||||
|
||||
always_comb begin
|
||||
mdio_write = 1'b0;
|
||||
mdio_phy_address = 5'd1;
|
||||
mdio_register_address = 5'h11;
|
||||
mdio_write_data = 16'd0;
|
||||
new_link_ready = (mdio_read_data[15:14] == 2'b10) &&
|
||||
mdio_read_data[13] && mdio_read_data[11] && mdio_read_data[10];
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= I_WAIT_RESET;
|
||||
timer <= '0;
|
||||
mdio_start <= 1'b0;
|
||||
initialization_complete <= 1'b0;
|
||||
link_ready <= 1'b0;
|
||||
link_down_events <= 64'd0;
|
||||
end else begin
|
||||
mdio_start <= 1'b0;
|
||||
case (state)
|
||||
I_WAIT_RESET: begin
|
||||
link_ready <= 1'b0;
|
||||
if (phy_reset_complete) begin timer <= '0; state <= I_SETTLE; end
|
||||
end
|
||||
I_SETTLE: begin
|
||||
if (timer == SETTLE_CYCLES-1) begin timer <= '0; state <= I_START; end
|
||||
else timer <= timer + {{(TIMER_W-1){1'b0}},1'b1};
|
||||
end
|
||||
I_START: begin
|
||||
if (!mdio_busy) begin mdio_start <= 1'b1; state <= I_WAIT_MDIO; end
|
||||
end
|
||||
I_WAIT_MDIO: begin
|
||||
if (mdio_done) begin
|
||||
initialization_complete <= 1'b1;
|
||||
if (link_ready && !new_link_ready)
|
||||
link_down_events <= link_down_events + 64'd1;
|
||||
link_ready <= new_link_ready;
|
||||
timer <= '0;
|
||||
state <= I_POLL_WAIT;
|
||||
end
|
||||
end
|
||||
I_POLL_WAIT: begin
|
||||
if (timer == POLL_CYCLES-1) begin timer <= '0; state <= I_START; end
|
||||
else timer <= timer + {{(TIMER_W-1){1'b0}},1'b1};
|
||||
end
|
||||
default: state <= I_WAIT_RESET;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,39 @@
|
||||
`default_nettype none
|
||||
|
||||
module rgmii_tx (
|
||||
input wire logic gmii_clk,
|
||||
input wire logic tx_clk90,
|
||||
input wire logic [7:0] gmii_txd,
|
||||
input wire logic gmii_tx_en,
|
||||
input wire logic gmii_tx_er,
|
||||
output logic rgmii_txc,
|
||||
output logic [3:0] rgmii_txd,
|
||||
output logic rgmii_tx_ctl
|
||||
);
|
||||
`ifdef SYNTHESIS
|
||||
genvar i;
|
||||
generate for (i = 0; i < 4; i = i + 1) begin : g_txd_oddr
|
||||
ODDR #(.DDR_CLK_EDGE("SAME_EDGE"), .INIT(1'b0), .SRTYPE("SYNC")) u_oddr (
|
||||
.C(gmii_clk), .CE(1'b1), .D1(gmii_txd[i]), .D2(gmii_txd[i+4]),
|
||||
.Q(rgmii_txd[i]), .R(1'b0), .S(1'b0)
|
||||
);
|
||||
end endgenerate
|
||||
ODDR #(.DDR_CLK_EDGE("SAME_EDGE"), .INIT(1'b0), .SRTYPE("SYNC")) u_ctl_oddr (
|
||||
.C(gmii_clk), .CE(1'b1), .D1(gmii_tx_en), .D2(gmii_tx_en ^ gmii_tx_er),
|
||||
.Q(rgmii_tx_ctl), .R(1'b0), .S(1'b0)
|
||||
);
|
||||
ODDR #(.DDR_CLK_EDGE("SAME_EDGE"), .INIT(1'b0), .SRTYPE("SYNC")) u_txc_oddr (
|
||||
.C(tx_clk90), .CE(1'b1), .D1(1'b1), .D2(1'b0),
|
||||
.Q(rgmii_txc), .R(1'b0), .S(1'b0)
|
||||
);
|
||||
`else
|
||||
// Edge-select behavioral model; hardware uses the ODDR primitives above.
|
||||
always_comb begin
|
||||
rgmii_txd = gmii_clk ? gmii_txd[3:0] : gmii_txd[7:4];
|
||||
rgmii_tx_ctl = gmii_clk ? gmii_tx_en : (gmii_tx_en ^ gmii_tx_er);
|
||||
rgmii_txc = tx_clk90;
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,93 @@
|
||||
# Non-project Vivado build for the Genesys 2 MAX2771 recorder.
|
||||
# Run from repository root:
|
||||
# vivado -mode batch -source targets/genesys2/scripts/build_vivado.tcl
|
||||
|
||||
set script_dir [file normalize [file dirname [info script]]]
|
||||
set target_dir [file normalize [file join $script_dir ..]]
|
||||
set root_dir [file normalize [file join $target_dir .. ..]]
|
||||
set build_dir [file join $root_dir build genesys2 vivado]
|
||||
set report_dir [file join $root_dir build genesys2 reports]
|
||||
file mkdir $build_dir
|
||||
file mkdir $report_dir
|
||||
|
||||
set part xc7k325tffg900-2
|
||||
set top max2771_recorder_top
|
||||
create_project -in_memory -part $part
|
||||
set_property target_language Verilog [current_project]
|
||||
set_property default_lib work [current_project]
|
||||
set_property verilog_define {SYNTHESIS=1} [current_fileset]
|
||||
|
||||
set common_rtl [lsort [glob -nocomplain [file join $root_dir rtl common *.sv]]]
|
||||
set target_rtl [lsort [glob -nocomplain [file join $target_dir rtl *.sv]]]
|
||||
set rtl_files [concat $common_rtl $target_rtl]
|
||||
if {[llength $rtl_files] == 0} { error "No RTL files found" }
|
||||
read_verilog -sv $rtl_files
|
||||
read_xdc [file join $target_dir constraints genesys2_max2771_recorder.xdc]
|
||||
|
||||
synth_design -top $top -part $part -flatten_hierarchy rebuilt
|
||||
write_checkpoint -force [file join $build_dir post_synth.dcp]
|
||||
report_utilization -file [file join $report_dir post_synth_utilization.rpt]
|
||||
report_clocks -file [file join $report_dir clocks.rpt]
|
||||
report_clock_utilization -file [file join $report_dir clock_utilization.rpt]
|
||||
check_timing -verbose -file [file join $report_dir check_timing_post_synth.rpt]
|
||||
report_cdc -details -file [file join $report_dir cdc_post_synth.rpt]
|
||||
|
||||
# Fail for registers lacking a clock. I/O delay omissions are separately and
|
||||
# deliberately documented because the MAX2771 tCO/board skew is not yet known.
|
||||
set no_clock_text [check_timing -override_defaults no_clock -return_string]
|
||||
if {[regexp -nocase {([1-9][0-9]*) register[^\n]*no clock} $no_clock_text]} {
|
||||
error "Unconstrained sequential clocks detected; see check_timing_post_synth.rpt"
|
||||
}
|
||||
|
||||
# Vivado exposes CDC violations as objects after report_cdc. Any Critical or
|
||||
# Warning CDC not waived by structural ASYNC_REG recognition stops the build.
|
||||
if {[llength [info commands get_cdc_violations]] != 0} {
|
||||
set major_cdc [get_cdc_violations -quiet -filter {SEVERITY == "Critical" || SEVERITY == "Warning"}]
|
||||
set unexpected_cdc 0
|
||||
foreach violation $major_cdc {
|
||||
# CDC-6 is Vivado's expected classification for a multi-bit Gray bus
|
||||
# whose first two destination stages carry ASYNC_REG. Only the three
|
||||
# named Gray synchronizers are accepted; every other warning is fatal.
|
||||
set description [report_property -return_string $violation]
|
||||
if {![regexp {u_sample_fifo/(wgray_rsync1|rgray_wsync1)|u_drop_sync/gray_sync1} $description]} {
|
||||
puts stderr "Unexpected CDC violation:\n$description"
|
||||
set unexpected_cdc 1
|
||||
}
|
||||
}
|
||||
if {$unexpected_cdc} { error "Major unexpected CDC warnings detected; see cdc_post_synth.rpt" }
|
||||
}
|
||||
|
||||
opt_design
|
||||
place_design
|
||||
phys_opt_design
|
||||
write_checkpoint -force [file join $build_dir post_place.dcp]
|
||||
report_timing_summary -delay_type min_max -report_unconstrained -check_timing_verbose \
|
||||
-file [file join $report_dir timing_post_place.rpt]
|
||||
|
||||
route_design
|
||||
phys_opt_design
|
||||
write_checkpoint -force [file join $build_dir post_route.dcp]
|
||||
report_timing_summary -delay_type min_max -report_unconstrained -check_timing_verbose \
|
||||
-file [file join $report_dir timing_post_route.rpt]
|
||||
report_timing -delay_type max -max_paths 50 -sort_by group \
|
||||
-file [file join $report_dir setup_paths.rpt]
|
||||
report_timing -delay_type min -max_paths 50 -sort_by group \
|
||||
-file [file join $report_dir hold_paths.rpt]
|
||||
report_utilization -hierarchical -file [file join $report_dir utilization_post_route.rpt]
|
||||
report_methodology -file [file join $report_dir methodology_post_route.rpt]
|
||||
report_drc -file [file join $report_dir drc_post_route.rpt]
|
||||
|
||||
set setup_paths [get_timing_paths -quiet -delay_type max -max_paths 1]
|
||||
set hold_paths [get_timing_paths -quiet -delay_type min -max_paths 1]
|
||||
if {[llength $setup_paths] == 0 || [llength $hold_paths] == 0} {
|
||||
error "Timing paths unavailable; clocks may be unconstrained"
|
||||
}
|
||||
set setup_slack [get_property SLACK [lindex $setup_paths 0]]
|
||||
set hold_slack [get_property SLACK [lindex $hold_paths 0]]
|
||||
if {$setup_slack < 0.0 || $hold_slack < 0.0} {
|
||||
error "Timing failed: setup WNS=$setup_slack ns, hold WNS=$hold_slack ns"
|
||||
}
|
||||
|
||||
write_bitstream -force [file join $build_dir max2771_recorder.bit]
|
||||
puts "BUILD PASSED: setup WNS=$setup_slack ns, hold WNS=$hold_slack ns"
|
||||
puts "Bitstream: [file join $build_dir max2771_recorder.bit]"
|
||||
@@ -0,0 +1,24 @@
|
||||
# Run: vivado -mode batch -source targets/genesys2/scripts/program_fpga.tcl -tclargs [bitstream]
|
||||
set script_dir [file normalize [file dirname [info script]]]
|
||||
set target_dir [file normalize [file join $script_dir ..]]
|
||||
set root_dir [file normalize [file join $target_dir .. ..]]
|
||||
if {$argc > 0} {
|
||||
set bitstream [file normalize [lindex $argv 0]]
|
||||
} else {
|
||||
set bitstream [file join $root_dir build genesys2 vivado max2771_recorder.bit]
|
||||
}
|
||||
if {![file exists $bitstream]} { error "Bitstream not found: $bitstream" }
|
||||
|
||||
open_hw_manager
|
||||
connect_hw_server
|
||||
open_hw_target
|
||||
set devices [get_hw_devices -quiet -filter {PART =~ "xc7k325t*"}]
|
||||
if {[llength $devices] == 0} { error "No xc7k325t JTAG device found" }
|
||||
set device [lindex $devices 0]
|
||||
current_hw_device $device
|
||||
refresh_hw_device -update_hw_probes false $device
|
||||
set_property PROGRAM.FILE $bitstream $device
|
||||
program_hw_devices $device
|
||||
refresh_hw_device $device
|
||||
puts "Programmed $device with $bitstream"
|
||||
close_hw_manager
|
||||
Reference in New Issue
Block a user