diff --git a/Makefile b/Makefile index c34ebed6..297b666e 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,6 @@ go: cd build; cmake ..; make -j$(JOBS) format: - find . -name \*.cc -and -not -path './third_party/*' -exec $(CLANG_FORMAT) -style=file -i {} \; - find . -name \*.h -and -not -path './third_party/*' -exec $(CLANG_FORMAT) -style=file -i {} \; - find . -name \*.py -and -not -path './third_party/*' -exec yapf -p -i {} \; + find . -name \*.cc -and -not -path './third_party/*' -and -not -path './.git/*' -exec $(CLANG_FORMAT) -style=file -i {} \; + find . -name \*.h -and -not -path './third_party/*' -and -not -path './.git/*' -exec $(CLANG_FORMAT) -style=file -i {} \; + find . -name \*.py -and -not -path './third_party/*' -and -not -path './.git/*' -exec yapf -p -i {} \; diff --git a/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h b/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h index 73d6c134..ae0634d7 100644 --- a/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h +++ b/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h @@ -25,8 +25,8 @@ class BitstreamWriter { typedef std::array header_t; typedef std::vector packets_t; // Only defined if a packet exists - typedef absl::optional> op_data_t; - typedef absl::Span::iterator data_iterator_t; + typedef absl::optional> op_data_t; + typedef absl::Span::iterator data_iterator_t; using itr_value_type = uint32_t; class packet_iterator diff --git a/lib/include/prjxray/xilinx/xc7series/configuration.h b/lib/include/prjxray/xilinx/xc7series/configuration.h index fa10a19d..20dea9c3 100644 --- a/lib/include/prjxray/xilinx/xc7series/configuration.h +++ b/lib/include/prjxray/xilinx/xc7series/configuration.h @@ -16,7 +16,7 @@ namespace xc7series { class Configuration { public: - using FrameMap = std::map>; + using FrameMap = std::map>; template static absl::optional InitWithPackets( @@ -28,7 +28,7 @@ class Configuration { : part_(part) { for (auto& frame : *frames) { frames_[frame.first] = - absl::Span(frame.second); + absl::Span(frame.second); } } diff --git a/lib/include/prjxray/xilinx/xc7series/configuration_packet.h b/lib/include/prjxray/xilinx/xc7series/configuration_packet.h index 80cfe9bb..f60d370b 100644 --- a/lib/include/prjxray/xilinx/xc7series/configuration_packet.h +++ b/lib/include/prjxray/xilinx/xc7series/configuration_packet.h @@ -27,7 +27,7 @@ class ConfigurationPacket { ConfigurationPacket(unsigned int header_type, Opcode opcode, ConfigurationRegister address, - const absl::Span& data) + const absl::Span& data) : header_type_(header_type), opcode_(opcode), address_(address), @@ -47,13 +47,13 @@ class ConfigurationPacket { unsigned int header_type() const { return header_type_; } const Opcode opcode() const { return opcode_; } const ConfigurationRegister address() const { return address_; } - const absl::Span& data() const { return data_; } + const absl::Span& data() const { return data_; } private: unsigned int header_type_; Opcode opcode_; ConfigurationRegister address_; - absl::Span data_; + absl::Span data_; }; std::ostream& operator<<(std::ostream& o, const ConfigurationPacket& packet); diff --git a/lib/xilinx/xc7series/configuration_packet.cc b/lib/xilinx/xc7series/configuration_packet.cc index f95fc4ae..bb241715 100644 --- a/lib/xilinx/xc7series/configuration_packet.cc +++ b/lib/xilinx/xc7series/configuration_packet.cc @@ -25,7 +25,11 @@ ConfigurationPacket::InitWithWords(absl::Span words, // NOPs. Since Type 0 packets don't exist according to // UG470 and they seem to be zero-filled, just consume // the bytes without generating a packet. - return {words.subspan(1), {}}; + return {words.subspan(1), + {{header_type, + Opcode::NOP, + ConfigurationRegister::CRC, + {}}}}; case 0x1: { Opcode opcode = static_cast( bit_field_get(words[0], 28, 27)); @@ -73,6 +77,10 @@ ConfigurationPacket::InitWithWords(absl::Span words, } std::ostream& operator<<(std::ostream& o, const ConfigurationPacket& packet) { + if (packet.header_type() == 0x0) { + return o << "[Zero-pad]" << std::endl; + } + switch (packet.opcode()) { case ConfigurationPacket::Opcode::NOP: o << "[NOP]" << std::endl; diff --git a/minitests/partial_reconfig_flow/Makefile b/minitests/partial_reconfig_flow/Makefile new file mode 100644 index 00000000..75916b30 --- /dev/null +++ b/minitests/partial_reconfig_flow/Makefile @@ -0,0 +1,95 @@ +# Top-level target for generating a programmable bitstream. Given a .fasm +# file, calling make with the .fasm extension replaced with .hand_crafted.bit +# will generate a bitstream that includes both the harness and the .fasm design +# ready for programming to a board. For example, 'make +# roi_noninv.hand_crafted.bit' will generate a bitstream that includes the +# design from roi_noninv.fasm. +%.hand_crafted.bit: init_sequence.bit %.no_headers.bin final_sequence.bin + cat $^ > $@ + +%.no_headers.bin: %.patched.bin + # WARNING: these values need to be tweaked if anything about the + # Vivado-generated design changes. + xxd -p -s 0x18 $< | xxd -r -p - $@ + +%.patched.bin: %.frm harness_routed.bit + ${XRAY_TOOLS_DIR}/xc7patch \ + --part_file ${XRAY_PART_YAML} \ + --bitstream_file harness_routed.bit \ + --frm_file $< \ + --output_file $@ + +# xc7patch currently only generates the actual frame writes which is +# insufficient to program a device. Grab the initialization and finalization +# sequences from the harness bitstream so they can be tacked on to the +# xc7patch-generated bitstream to create a programmable bitstream. +# +# The offsets used below were determined by manually inspecting +# harness_routed.bit with a hex editor. init_sequence.bit is the beginning of +# the file until just before the actual frame data is sent via a write to FDRI. +# final_sequence.bin is from just after the frame data write to the end of the +# file. Note that final_sequence.bin normally includes at least one CRC check. +# The sed command replaces any CRC checks with a Reset CRC command which is the +# same behavior as setting BITSTREAM.GENERAL.CRC to Disabled. These offset +# should not change unless you alter the bitstream format used (i.e. setting +# BITSTREAM.GENERAL.DEBUGBITSTREAM or BITSTREAM.GENERAL.PERFRAMECRC to YES). +init_sequence.bit: harness_routed.bit + # WARNING: these values need to be tweaked if anything about the + # Vivado-generated design changes. + xxd -p -l 0x147 $< | xxd -r -p - $@ + +final_sequence.bin: harness_routed.bit + # WARNING: these values need to be tweaked if anything about the + # Vivado-generated design changes. + xxd -p -s 0x216abf $< | \ + tr -d '\n' | \ + sed -e 's/30000001.\{8\}/3000800100000007/g' | \ + fold -w 40 | \ + xxd -r -p - $@ + +# Generate a suitable harness by using Vivado's partial reconfiguration +# feature. roi_inv is used as a sample reconfiguration design as one is +# required to generate a partial reconfiguration design. +harness.dcp: harness.tcl top.v roi_base.v + vivado -mode batch -source harness.tcl + +roi_inv.dcp: roi_inv.tcl roi_inv.v + vivado -mode batch -source roi_inv.tcl + +roi_inv_routed.dcp roi_inv_w_harness_routed.dcp harness_routed.dcp: harness.dcp roi_inv.dcp roi_inv_routed.tcl + vivado -mode batch -source roi_inv_routed.tcl + +# Conversions between various formats. +%.bit: %.dcp write_bitstream.tcl + vivado -mode batch -source write_bitstream.tcl -tclargs $< $@ + +%.bits: %.bit + ${XRAY_BITREAD} -y -o $@ $< + +# Extract only bits that are within the ROI. +%.roi.bits: %.bit + ${XRAY_BITREAD} -F ${XRAY_ROI_FRAMES} -z -y -o $@ $< + +%.segp: %.roi.bits + ${XRAY_SEGPRINT} -zd $< > $@ + +%.fasm: %.segp + ${XRAY_DIR}/tools/segprint2fasm.py $< $@ + +%.frm: %.fasm + ${XRAY_DIR}/tools/fasm2frame.py $< $@ + +# This format is a human-readable representation of the configuration packets +# used to interact with 7-series chips over JTAG. +%.packets: %.bit + ${XRAY_TOOLS_DIR}/bittool list_config_packets $< > $@ + +clean: + rm -rf specimen_[0-9][0-9][0-9]/ seg_clblx.segbits vivado*.log vivado_*.str vivado*.jou design *.bits *.dcp *.bit design.txt .Xil + rm -rf out_* *~ + rm -rf *.frm *.segp *.packets *.bin + rm -rf harness_routed.fasm roi_inv_w_harness_routed.fasm + rm -rf hd_visual + +.PHONY: clean + diff --git a/minitests/partial_reconfig_flow/README.md b/minitests/partial_reconfig_flow/README.md new file mode 100644 index 00000000..8eac3df0 --- /dev/null +++ b/minitests/partial_reconfig_flow/README.md @@ -0,0 +1,53 @@ +# FASM Proof of Concept using Vivado Partial Reconfig flow + +top.v is a top-level design that routes a variety of signal into a black-box +region of interest (ROI). Vivado's Partial Reconfiguration flow (see UG909 +and UG947) is used to implement that design and obtain a bitstream that +configures portions of the chip that are currently undocumented. + +Designs that fit within the ROI are written in FASM and merged with the above +harness into a bitstream with fasm2frame and xc7patch. + +# Usage + +make rules are provided for generating each step of the process so that +intermediate forms can be analyzed. Assuming you have a .fasm file, invoking +the %.hand\_crafted.bit rule will generate a merged bitstream: + +``` +make foo.hand\_crafted.bit # reads foo.fasm +``` + +# Using Vivado to generate .fasm + +Vivado's Partial Reconfiguration flow can be used to synthesize and implement a +design that is then converted to .fasm. The basic process is to write a module +that _exactly_ matches the roi blackbox in the top-level design. Note that +even the name of the module must match exactly. Once you have a design, the +first step is to synthesize the design with -mode out\_of\_context: + +``` +read_verilog .v +synth_design -mode out_of_context -top roi -part $::env(XRAY_PART) +write_checkpoint -force .dcp +``` + +Next, implement that design within the harness. Run 'make harness\_routed.dcp' +if it doesn't already exist. The following TCL will load the fully-routed +harness, load your synthesized design, and generate a bitstream containing +both: +``` +open_checkpoint -force harness_routed.dcp +read_checkpoint -cell .dcp +opt_design +place_design +route_design +write_checkpoint -force _routed.dcp +write_bitstream -force _routed.bit +``` + +'make \_routed.fasm' will run a sequence of tools to extract the bits +that are inside the ROI and convert them to FASM. The resulting .fasm can be +used to generate a marged bitstream using +'make \_routed.hand\_crafted.bit'. The resulting bitstream should be +equivalent to \_routed.bit. diff --git a/minitests/partial_reconfig_flow/defines.v b/minitests/partial_reconfig_flow/defines.v new file mode 100644 index 00000000..1008d558 --- /dev/null +++ b/minitests/partial_reconfig_flow/defines.v @@ -0,0 +1,8 @@ +`ifndef DIN_N +`define DIN_N 8 +`endif + +`ifndef DOUT_N +`define DOUT_N 8 +`endif + diff --git a/minitests/partial_reconfig_flow/harness.tcl b/minitests/partial_reconfig_flow/harness.tcl new file mode 100644 index 00000000..14be2e3c --- /dev/null +++ b/minitests/partial_reconfig_flow/harness.tcl @@ -0,0 +1,5 @@ +read_verilog top.v +read_verilog roi_base.v + +synth_design -top top -part $::env(XRAY_PART) +write_checkpoint -force harness.dcp diff --git a/minitests/partial_reconfig_flow/roi_base.v b/minitests/partial_reconfig_flow/roi_base.v new file mode 100644 index 00000000..ec693408 --- /dev/null +++ b/minitests/partial_reconfig_flow/roi_base.v @@ -0,0 +1,9 @@ +//See README and tcl for more info + +`include "defines.v" + +module roi(input clk, + input [DIN_N-1:0] din, output [DOUT_N-1:0] dout); + parameter DIN_N = `DIN_N; + parameter DOUT_N = `DOUT_N; +endmodule diff --git a/minitests/partial_reconfig_flow/roi_inv.tcl b/minitests/partial_reconfig_flow/roi_inv.tcl new file mode 100644 index 00000000..a963234d --- /dev/null +++ b/minitests/partial_reconfig_flow/roi_inv.tcl @@ -0,0 +1,3 @@ +read_verilog roi_inv.v +synth_design -mode out_of_context -top roi -part $::env(XRAY_PART) +write_checkpoint -force roi_inv.dcp diff --git a/minitests/partial_reconfig_flow/roi_inv.v b/minitests/partial_reconfig_flow/roi_inv.v new file mode 100644 index 00000000..7e575cb5 --- /dev/null +++ b/minitests/partial_reconfig_flow/roi_inv.v @@ -0,0 +1,53 @@ +//Connect the switches to the LEDs, inverting the signal in the ROI +//Assumes # inputs = # outputs + +`include "defines.v" + +module roi(input clk, + input [DIN_N-1:0] din, output [DOUT_N-1:0] dout); + parameter DIN_N = `DIN_N; + parameter DOUT_N = `DOUT_N; + wire [DIN_N-1:0] internal; + + genvar i; + generate + //CLK + (* KEEP, DONT_TOUCH *) + reg clk_reg; + always @(posedge clk) begin + clk_reg <= clk_reg; + end + + //DIN + for (i = 0; i < DIN_N; i = i+1) begin:ins + //Very expensive inverter + (* KEEP, DONT_TOUCH *) + LUT6 #( + .INIT(64'b01) + ) lut ( + .I0(din[i]), + .I1(1'b0), + .I2(1'b0), + .I3(1'b0), + .I4(1'b0), + .I5(1'b0), + .O(internal[i])); + end + + //DOUT + for (i = 0; i < DOUT_N; i = i+1) begin:outs + //Very expensive buffer + (* KEEP, DONT_TOUCH *) + LUT6 #( + .INIT(64'b010) + ) lut ( + .I0(internal[i]), + .I1(1'b0), + .I2(1'b0), + .I3(1'b0), + .I4(1'b0), + .I5(1'b0), + .O(dout[i])); + end + endgenerate +endmodule diff --git a/minitests/partial_reconfig_flow/roi_inv_routed.tcl b/minitests/partial_reconfig_flow/roi_inv_routed.tcl new file mode 100644 index 00000000..0fee8ca2 --- /dev/null +++ b/minitests/partial_reconfig_flow/roi_inv_routed.tcl @@ -0,0 +1,149 @@ +open_checkpoint harness.dcp + +create_pblock roi +add_cells_to_pblock [get_pblocks roi] [get_cells roi] +resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)" + +set_property CFGBVS VCCO [current_design] +set_property CONFIG_VOLTAGE 3.3 [current_design] + +# Number of package inputs going to ROI +set DIN_N 8 +# Number of ROI outputs going to package +set DOUT_N 8 + +set part $::env(XRAY_PART) +set pincfg $::env(XRAY_PINCFG) + +# Map of top level net names to IOB pin names +array set net2pin [list] + +# Create pin assignments based on what we are targetting +# A50T I/O Bank 16 sequential layout +if {$part eq "xc7a50tfgg484-1"} { + # Partial list, expand as needed + set bank_16 "F21 G22 G21 D21 E21 D22 E22 A21 B21 B22 C22 C20 D20 F20 F19 A19 A18" + set banki 0 + + # CLK + set pin [lindex $bank_16 $banki] + incr banki + set net2pin(clk) $pin + + # DIN + for {set i 0} {$i < $DIN_N} {incr i} { + set pin [lindex $bank_16 $banki] + incr banki + set net2pin(din[$i]) $pin + } + + # DOUT + for {set i 0} {$i < $DOUT_N} {incr i} { + set pin [lindex $bank_16 $banki] + incr banki + set net2pin(dout[$i]) $pin + } +} elseif {$part eq "xc7a35tcsg324-1"} { + # Arty A7 switch, button, and LED + if {$pincfg eq "ARTY-A7-SWBUT"} { + # https://reference.digilentinc.com/reference/programmable-logic/arty/reference-manual?redirect=1 + # 4 switches then 4 buttons + set sw_but "A8 C11 C10 A10 D9 C9 B9 B8" + # 4 LEDs then 4 RGB LEDs (green only) + set leds "H5 J5 T9 T10 F6 J4 J2 H6" + + # 100 MHz CLK onboard + set pin "E3" + set net2pin(clk) $pin + + # DIN + for {set i 0} {$i < $DIN_N} {incr i} { + set pin [lindex $sw_but $i] + set net2pin(din[$i]) $pin + } + + # DOUT + for {set i 0} {$i < $DOUT_N} {incr i} { + set pin [lindex $leds $i] + set net2pin(dout[$i]) $pin + } + # Arty A7 pmod + # Disabled per above + } elseif {$pincfg eq "ARTY-A7-PMOD"} { + # https://reference.digilentinc.com/reference/programmable-logic/arty/reference-manual?redirect=1 + set pmod_ja "G13 B11 A11 D12 D13 B18 A18 K16" + set pmod_jb "E15 E16 D15 C15 J17 J18 K15 J15" + set pmod_jc "U12 V12 V10 V11 U14 V14 T13 U13" + + # CLK on Pmod JA + set pin [lindex $pmod_ja 0] + set net2pin(clk) $pin + + # DIN on Pmod JB + for {set i 0} {$i < $DIN_N} {incr i} { + set pin [lindex $pmod_jb $i] + set net2pin(din[$i]) $pin + } + + # DOUT on Pmod JC + for {set i 0} {$i < $DOUT_N} {incr i} { + set pin [lindex $pmod_jc $i] + set net2pin(dout[$i]) $pin + } + } else { + error "Unsupported config $pincfg" + } +} elseif {$part eq "xc7a35tcpg236-1"} { + if {$pincfg eq "BASYS3-SWBUT"} { + # https://raw.githubusercontent.com/Digilent/digilent-xdc/master/Basys-3-Master.xdc + + # Slide switches + set sws "V17 V16 W16 W17 W15 V15 W14 W13 V2 T3 T2 R3 W2 U1 T1 R2" + set leds "U16 E19 U19 V19 W18 U15 U14 V14 V13 V3 W3 U3 P3 N3 P1 L1" + + # 100 MHz CLK onboard + set pin "W5" + set net2pin(clk) $pin + + # DIN + for {set i 0} {$i < $DIN_N} {incr i} { + set pin [lindex $sws $i] + set net2pin(din[$i]) $pin + } + + # DOUT + for {set i 0} {$i < $DOUT_N} {incr i} { + set pin [lindex $leds $i] + set net2pin(dout[$i]) $pin + } + } else { + error "Unsupported config $pincfg" + } +} else { + error "Pins: unsupported part $part" +} + +# Now actually apply the pin definitions +puts "Applying pin definitions" +foreach {net pin} [array get net2pin] { + puts " Net $net to pin $pin" + set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS33" [get_ports $net] +} + +set_property HD.RECONFIGURABLE TRUE [get_cells roi] + +read_checkpoint -cell roi roi_inv.dcp + +opt_design +place_design +route_design + +write_checkpoint -force roi_inv_w_harness_routed.dcp + +# Routed design of roi cell only +write_checkpoint -force -cell roi roi_inv_routed.dcp + +# Replace roi cell with a black box and write the rest of the design +update_design -cell roi -black_box +lock_design -level routing +write_checkpoint -force harness_routed.dcp diff --git a/minitests/partial_reconfig_flow/roi_noninv.fasm b/minitests/partial_reconfig_flow/roi_noninv.fasm new file mode 100644 index 00000000..7eaf102b --- /dev/null +++ b/minitests/partial_reconfig_flow/roi_noninv.fasm @@ -0,0 +1,228 @@ +INT_L_X10Y107.CENTER_INTER_L.SE6BEG0 NN6END0 +INT_L_X10Y104.CENTER_INTER_L.WL1BEG0 NW2END2 +INT_L_X10Y103.CENTER_INTER_L.EL1BEG0 NR1END1 +INT_L_X10Y103.CENTER_INTER_L.EL1BEG_N3 NW2END0 +INT_L_X10Y103.CENTER_INTER_L.FAN_ALT4 NR1END0 +INT_L_X10Y103.CENTER_INTER_L.NE2BEG3 NL1BEG_N3 +INT_L_X10Y103.CENTER_INTER_L.NL1BEG_N3 NL1END0 +INT_L_X10Y103.CENTER_INTER_L.SE2BEG2 EL1END2 +INT_L_X10Y103.CENTER_INTER_L.WW2BEG2 WL1END2 +CLBLM_L_X10Y102.SLICE_X13Y102.ALUT.INIT[01] 1 +CLBLM_L_X10Y102.SLICE_X13Y102.BLUT.INIT[01] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[01] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.BLUT.INIT[01] 1 +INT_L_X10Y102.CENTER_INTER_L.BYP_ALT5 NN2END2 +INT_L_X10Y102.CENTER_INTER_L.EE2BEG2 NL1END2 +INT_L_X10Y102.CENTER_INTER_L.EL1BEG_N3 NL1END0 +INT_L_X10Y102.CENTER_INTER_L.GFAN0 GND_WIRE +INT_L_X10Y102.CENTER_INTER_L.GFAN1 GND_WIRE +INT_L_X10Y102.CENTER_INTER_L.IMUX_L0 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L1 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L10 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L11 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L12 GFAN1 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L13 GFAN1 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L14 NL1BEG_N3 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L15 FAN_BOUNCE_S3_4 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L16 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L17 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L18 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L19 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L2 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L24 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L25 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L26 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L27 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L3 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L4 GFAN1 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L5 GFAN1 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L6 WR1END3 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L7 BYP_BOUNCE5 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L8 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.IMUX_L9 GFAN0 +INT_L_X10Y102.CENTER_INTER_L.NL1BEG0 LOGIC_OUTS_L9 +INT_L_X10Y102.CENTER_INTER_L.NL1BEG_N3 LOGIC_OUTS_L8 +INT_L_X10Y102.CENTER_INTER_L.NR1BEG0 LOGIC_OUTS_L12 +INT_L_X10Y102.CENTER_INTER_L.NR1BEG1 LOGIC_OUTS_L13 +INT_L_X10Y102.CENTER_INTER_L.SE2BEG2 EL1END2 +INT_L_X10Y102.CENTER_INTER_L.SR1BEG2 WL1END1 +INT_L_X10Y102.CENTER_INTER_L.SW2BEG3 WL1END3 +INT_L_X10Y102.CENTER_INTER_L.WW2BEG2 WL1END2 +CLBLM_L_X10Y101.SLICE_X13Y101.ALUT.INIT[01] 1 +CLBLM_L_X10Y101.SLICE_X13Y101.BLUT.INIT[01] 1 +CLBLM_L_X10Y101.SLICE_X12Y101.ALUT.INIT[01] 1 +CLBLM_L_X10Y101.SLICE_X12Y101.BLUT.INIT[01] 1 +INT_L_X10Y101.CENTER_INTER_L.BYP_ALT3 NL1BEG_N3 +INT_L_X10Y101.CENTER_INTER_L.BYP_ALT4 NW2END1 +INT_L_X10Y101.CENTER_INTER_L.ER1BEG2 LOGIC_OUTS_L13 +INT_L_X10Y101.CENTER_INTER_L.GFAN0 GND_WIRE +INT_L_X10Y101.CENTER_INTER_L.GFAN1 GND_WIRE +INT_L_X10Y101.CENTER_INTER_L.IMUX_L0 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L1 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L10 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L11 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L12 GFAN1 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L13 GFAN1 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L14 SR1END2 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L15 BYP_BOUNCE3 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L16 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L17 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L18 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L19 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L2 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L24 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L25 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L26 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L27 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L3 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L4 GFAN1 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L5 GFAN1 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L6 BYP_BOUNCE4 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L7 NW2END_S0_0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L8 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.IMUX_L9 GFAN0 +INT_L_X10Y101.CENTER_INTER_L.LV_L18 SR1BEG_S0 +INT_L_X10Y101.CENTER_INTER_L.NE2BEG1 NR1END1 +INT_L_X10Y101.CENTER_INTER_L.NE2BEG3 NN6END3 +INT_L_X10Y101.CENTER_INTER_L.NL1BEG0 LOGIC_OUTS_L9 +INT_L_X10Y101.CENTER_INTER_L.NL1BEG2 WL1END2 +INT_L_X10Y101.CENTER_INTER_L.NL1BEG_N3 LOGIC_OUTS_L8 +INT_L_X10Y101.CENTER_INTER_L.NN6BEG0 LOGIC_OUTS_L12 +INT_L_X10Y101.CENTER_INTER_L.SE2BEG2 EE2END2 +INT_L_X10Y101.CENTER_INTER_L.SR1BEG_S0 WL1END3 +CLBLM_L_X10Y100.SLICE_X13Y100.ALUT.INIT[01] 1 +CLBLM_L_X10Y100.SLICE_X13Y100.BLUT.INIT[01] 1 +CLBLM_L_X10Y100.SLICE_X12Y100.ALUT.INIT[01] 1 +INT_L_X10Y100.CENTER_INTER_L.BYP_ALT1 LOGIC_OUTS_L8 +INT_L_X10Y100.CENTER_INTER_L.BYP_ALT2 BYP_BOUNCE1 +INT_L_X10Y100.CENTER_INTER_L.BYP_ALT3 NL1BEG_N3 +INT_L_X10Y100.CENTER_INTER_L.EE2BEG3 NN6END3 +INT_L_X10Y100.CENTER_INTER_L.ER1BEG1 SR1BEG_S0 +INT_L_X10Y100.CENTER_INTER_L.GFAN0 GND_WIRE +INT_L_X10Y100.CENTER_INTER_L.GFAN1 GND_WIRE +INT_L_X10Y100.CENTER_INTER_L.IMUX_L0 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L1 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L10 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L11 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L13 GFAN1 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L14 BYP_BOUNCE2 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L16 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L19 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L2 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L25 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L26 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L3 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L4 GFAN1 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L5 GFAN1 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L6 SW2END2 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L7 BYP_BOUNCE3 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L8 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.IMUX_L9 GFAN0 +INT_L_X10Y100.CENTER_INTER_L.NE2BEG0 LOGIC_OUTS_L12 +INT_L_X10Y100.CENTER_INTER_L.NE2BEG3 NE6END3 +INT_L_X10Y100.CENTER_INTER_L.NL1BEG_N3 WL1END_N1_3 +INT_L_X10Y100.CENTER_INTER_L.NN2BEG2 WL1END1 +INT_L_X10Y100.CENTER_INTER_L.NR1BEG1 LOGIC_OUTS_L9 +INT_L_X10Y100.CENTER_INTER_L.SR1BEG_S0 WL1END3 +INT_R_X11Y104.CENTER_INTER_R.SL1BEG3 NE2END3 +INT_R_X11Y103.CENTER_INTER_R.NW2BEG2 NN2END2 +INT_R_X11Y103.CENTER_INTER_R.SL1BEG0 EL1END0 +INT_R_X11Y103.CENTER_INTER_R.WL1BEG2 SL1END3 +INT_R_X11Y103.CENTER_INTER_R.WL1BEG_N3 WR1END1 +CLBLM_R_X11Y102.SLICE_X14Y102.ALUT.INIT[01] 1 +CLBLM_R_X11Y102.SLICE_X14Y102.BLUT.INIT[01] 1 +INT_R_X11Y102.CENTER_INTER_R.EL1BEG0 LOGIC_OUTS13 +INT_R_X11Y102.CENTER_INTER_R.GFAN0 GND_WIRE +INT_R_X11Y102.CENTER_INTER_R.GFAN1 GND_WIRE +INT_R_X11Y102.CENTER_INTER_R.IMUX1 GFAN0 +INT_R_X11Y102.CENTER_INTER_R.IMUX11 GFAN0 +INT_R_X11Y102.CENTER_INTER_R.IMUX12 GFAN1 +INT_R_X11Y102.CENTER_INTER_R.IMUX15 EL1END3 +INT_R_X11Y102.CENTER_INTER_R.IMUX17 GFAN0 +INT_R_X11Y102.CENTER_INTER_R.IMUX18 GFAN0 +INT_R_X11Y102.CENTER_INTER_R.IMUX2 GFAN0 +INT_R_X11Y102.CENTER_INTER_R.IMUX24 GFAN0 +INT_R_X11Y102.CENTER_INTER_R.IMUX27 GFAN0 +INT_R_X11Y102.CENTER_INTER_R.IMUX4 GFAN1 +INT_R_X11Y102.CENTER_INTER_R.IMUX7 WR1END3 +INT_R_X11Y102.CENTER_INTER_R.IMUX8 GFAN0 +INT_R_X11Y102.CENTER_INTER_R.NW2BEG0 LOGIC_OUTS12 +INT_R_X11Y102.CENTER_INTER_R.SE2BEG1 NE2END1 +INT_R_X11Y102.CENTER_INTER_R.SL1BEG2 SE2END2 +INT_R_X11Y102.CENTER_INTER_R.SL1BEG3 NE2END3 +INT_R_X11Y102.CENTER_INTER_R.WL1BEG1 NW2END3 +INT_R_X11Y102.CENTER_INTER_R.WL1BEG2 WL1END3 +INT_R_X11Y102.CENTER_INTER_R.WL1BEG_N3 SL1END0 +INT_R_X11Y102.CENTER_INTER_R.WR1BEG3 NR1END2 +CLBLM_R_X11Y101.SLICE_X14Y101.ALUT.INIT[01] 1 +CLBLM_R_X11Y101.SLICE_X14Y101.AMUX.O6 1 +CLBLM_R_X11Y101.SLICE_X14Y101.BLUT.INIT[01] 1 +INT_R_X11Y101.CENTER_INTER_R.EE2BEG2 ER1END2 +INT_R_X11Y101.CENTER_INTER_R.EL1BEG0 LOGIC_OUTS13 +INT_R_X11Y101.CENTER_INTER_R.GFAN0 GND_WIRE +INT_R_X11Y101.CENTER_INTER_R.GFAN1 GND_WIRE +INT_R_X11Y101.CENTER_INTER_R.IMUX1 GFAN0 +INT_R_X11Y101.CENTER_INTER_R.IMUX11 GFAN0 +INT_R_X11Y101.CENTER_INTER_R.IMUX12 GFAN1 +INT_R_X11Y101.CENTER_INTER_R.IMUX15 EL1END3 +INT_R_X11Y101.CENTER_INTER_R.IMUX17 GFAN0 +INT_R_X11Y101.CENTER_INTER_R.IMUX18 GFAN0 +INT_R_X11Y101.CENTER_INTER_R.IMUX2 GFAN0 +INT_R_X11Y101.CENTER_INTER_R.IMUX24 GFAN0 +INT_R_X11Y101.CENTER_INTER_R.IMUX27 GFAN0 +INT_R_X11Y101.CENTER_INTER_R.IMUX4 GFAN1 +INT_R_X11Y101.CENTER_INTER_R.IMUX7 NR1END3 +INT_R_X11Y101.CENTER_INTER_R.IMUX8 GFAN0 +INT_R_X11Y101.CENTER_INTER_R.NN2BEG2 LOGIC_OUTS20 +INT_R_X11Y101.CENTER_INTER_R.NR1BEG2 SE2END2 +INT_R_X11Y101.CENTER_INTER_R.NW2BEG0 NE2END0 +INT_R_X11Y101.CENTER_INTER_R.SL1BEG3 NE2END3 +INT_R_X11Y101.CENTER_INTER_R.SR1BEG_S0 SL1END3 +INT_R_X11Y101.CENTER_INTER_R.SW2BEG2 SL1END2 +INT_R_X11Y101.CENTER_INTER_R.WL1BEG2 WR1END_S1_0 +INT_R_X11Y101.CENTER_INTER_R.WL1BEG_N3 SR1BEG_S0 +INT_R_X11Y101.CENTER_INTER_R.WW2BEG0 WL1END0 +INT_R_X11Y101.CENTER_INTER_R.WW2BEG1 WL1END1 +CLBLM_R_X11Y100.SLICE_X14Y100.ALUT.INIT[01] 1 +INT_R_X11Y100.CENTER_INTER_R.BYP_ALT5 ER1END1 +INT_R_X11Y100.CENTER_INTER_R.GFAN0 GND_WIRE +INT_R_X11Y100.CENTER_INTER_R.GFAN1 GND_WIRE +INT_R_X11Y100.CENTER_INTER_R.IMUX1 GFAN0 +INT_R_X11Y100.CENTER_INTER_R.IMUX11 GFAN0 +INT_R_X11Y100.CENTER_INTER_R.IMUX2 GFAN0 +INT_R_X11Y100.CENTER_INTER_R.IMUX4 GFAN1 +INT_R_X11Y100.CENTER_INTER_R.IMUX7 BYP_BOUNCE5 +INT_R_X11Y100.CENTER_INTER_R.IMUX8 GFAN0 +INT_R_X11Y100.CENTER_INTER_R.NL1BEG_N3 LOGIC_OUTS12 +INT_R_X11Y100.CENTER_INTER_R.NR1BEG3 NL1BEG_N3 +INT_R_X11Y100.CENTER_INTER_R.NW2BEG1 WL1END0 +INT_R_X11Y100.CENTER_INTER_R.SR1BEG_S0 SL1END3 +INT_R_X11Y100.CENTER_INTER_R.WL1BEG1 SE2END2 +INT_R_X11Y100.CENTER_INTER_R.WL1BEG_N3 SR1BEG_S0 +INT_L_X12Y103.CENTER_INTER_L.WL1BEG_N3 SE6END0 +INT_L_X12Y103.CENTER_INTER_L.WR1BEG1 NR1END0 +INT_L_X12Y102.CENTER_INTER_L.NR1BEG0 EL1END0 +INT_L_X12Y102.CENTER_INTER_L.WR1BEG3 EE2END2 +INT_L_X12Y101.CENTER_INTER_L.NW2BEG3 NR1END3 +INT_L_X12Y101.CENTER_INTER_L.SE2BEG0 EL1END0 +INT_L_X12Y101.CENTER_INTER_L.WL1BEG0 SE2END1 +INT_L_X12Y101.CENTER_INTER_L.WL1BEG1 WR1END3 +INT_L_X12Y101.CENTER_INTER_L.WR1BEG_S0 NE6END3 +INT_L_X12Y100.CENTER_INTER_L.EL1BEG2 NE6END3 +INT_L_X12Y100.CENTER_INTER_L.LV_L18 WR1END0 +INT_L_X12Y100.CENTER_INTER_L.NR1BEG3 EE2END3 +INT_L_X12Y100.CENTER_INTER_L.SW6BEG3 LV_L18 +INT_L_X12Y100.CENTER_INTER_L.WL1BEG0 WR1END2 +INT_R_X13Y101.CENTER_INTER_R.WR1BEG3 EE2END2 +INT_R_X13Y100.CENTER_INTER_R.SL1BEG2 EL1END2 +INT_R_X13Y100.CENTER_INTER_R.WL1BEG_N3 SE2END0 +INT_R_X13Y100.CENTER_INTER_R.WR1BEG2 NR1END1 +INT_L_X16Y100.CENTER_INTER_L.ER1BEG1 SR1BEG_S0 +INT_L_X16Y100.CENTER_INTER_L.SR1BEG_S0 WL1END3 +INT_R_X17Y101.CENTER_INTER_R.WL1BEG_N3 WW2END0 +CLBLL_R_X17Y100.SLICE_X27Y100.AFF.DMUX.AX 1 +CLBLL_R_X17Y100.SLICE_X27Y100.AFF.ZINI 1 +CLBLL_R_X17Y100.SLICE_X27Y100.AFF.ZRST 1 +CLBLL_R_X17Y100.SLICE_X27Y100.FFSYNC 1 +INT_R_X17Y100.CENTER_INTER_R.BYP_ALT0 LOGIC_OUTS0 +INT_R_X17Y100.CENTER_INTER_R.CLK0 ER1END1 diff --git a/minitests/partial_reconfig_flow/top.v b/minitests/partial_reconfig_flow/top.v new file mode 100644 index 00000000..997231a9 --- /dev/null +++ b/minitests/partial_reconfig_flow/top.v @@ -0,0 +1,14 @@ +//See README and tcl for more info + +`include "defines.v" + +module top(input wire clk, + input [DIN_N-1:0] din, output [DOUT_N-1:0] dout); + parameter DIN_N = `DIN_N; + parameter DOUT_N = `DOUT_N; + + roi #(.DIN_N(DIN_N), .DOUT_N(DOUT_N)) roi ( + .clk(clk), + .din(din), .dout(dout)); +endmodule + diff --git a/minitests/partial_reconfig_flow/write_bitstream.tcl b/minitests/partial_reconfig_flow/write_bitstream.tcl new file mode 100644 index 00000000..62beef9e --- /dev/null +++ b/minitests/partial_reconfig_flow/write_bitstream.tcl @@ -0,0 +1,27 @@ +open_checkpoint [lindex $argv 0] + +# Disabling CRC just replaces the CRC register writes with Reset CRC commands. +# This seems to work via JTAG as it works in combination with PERFRAMECRC +# either when applied via Vivado (this setting) or by manually patching the +# bitstream later. +# +set_property BITSTREAM.GENERAL.CRC Disable [current_design] + +# Debug bitstreams write to LOUT which is only valid on serial master/slave +# programming methods. If those are replaced with NOPs, Reset CRC commands, or +# removed entirely, the bitstream will program (DONE light goes active) but the +# configuration doesn't start. The JTAG status register shows BAD_PACKET_ERROR +# when this happens. I'm guessing that the individual frame writes require the +# PERFRAMECRC approach to work at all via JTAG. +# +#set_property BITSTREAM.GENERAL.DEBUGBITSTREAM YES [current_design] + +# PERFRAMECRC bitstreams can be directly loaded via JTAG. They also use an +# undocumented bit to disable autoincrement which seems to be required if doing +# individual frame writes instead of a bulk write. The CRC chceks after each +# frame are _required_ for this bitstream to program. +# +#set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design] + + +write_bitstream -force [lindex $argv 1] diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 558c7d83..06dc18c0 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -17,3 +17,10 @@ target_link_libraries(gen_part_base_yaml libprjxray yaml-cpp ) + +add_executable(xc7patch xc7patch.cc) +target_link_libraries(xc7patch + absl::strings + gflags + libprjxray +) diff --git a/tools/fasm2frame.py b/tools/fasm2frame.py index c29b9295..a21aa330 100755 --- a/tools/fasm2frame.py +++ b/tools/fasm2frame.py @@ -178,6 +178,8 @@ def run(f_in, f_out, sparse=False, debug=False): return '%s.%s.%s' % (tilej['type'], suffix, value) tile2dbkey = { + 'CLBLL_L': clb2dbkey, + 'CLBLL_R': clb2dbkey, 'CLBLM_L': clb2dbkey, 'CLBLM_R': clb2dbkey, 'INT_L': int2dbkey, diff --git a/tools/segprint2fasm.py b/tools/segprint2fasm.py index 4d4e37d5..d0732888 100755 --- a/tools/segprint2fasm.py +++ b/tools/segprint2fasm.py @@ -37,6 +37,8 @@ def tag2fasm(grid, seg, tag): which = m.group(1) value = m.group(2) site = { + 'clbll_l': 'CENTER_INTER_L', + 'clbll_r': 'CENTER_INTER_R', 'clblm_l': 'CENTER_INTER_L', 'clblm_r': 'CENTER_INTER_R', 'hclk_l': 'HCLK_L', @@ -56,6 +58,8 @@ def tag2fasm(grid, seg, tag): raise Exception("Couldn't find tile type %s" % tile_type) tag2asm = { + 'CLBLL_L': clbf, + 'CLBLL_R': clbf, 'CLBLM_L': clbf, 'CLBLM_R': clbf, 'INT_L': intf, diff --git a/tools/xc7patch.cc b/tools/xc7patch.cc new file mode 100644 index 00000000..3e0c549f --- /dev/null +++ b/tools/xc7patch.cc @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +DEFINE_string(part_file, "", "Definition file for target 7-series part"); +DEFINE_string(bitstream_file, + "", + "Initial bitstream to which the deltas are applied."); +DEFINE_string( + frm_file, + "", + "File containing a list of frame deltas to be applied to the base " + "bitstream. Each line in the file is of the form: " + " ,...,."); +DEFINE_string(output_file, "", "Write patched bitsteam to file"); + +namespace xc7series = prjxray::xilinx::xc7series; + +int main(int argc, char* argv[]) { + gflags::SetUsageMessage(argv[0]); + gflags::ParseCommandLineFlags(&argc, &argv, true); + + auto part = xc7series::Part::FromFile(FLAGS_part_file); + if (!part) { + std::cerr << "Part file not found or invalid" << std::endl; + return 1; + } + + auto bitstream_file = + prjxray::MemoryMappedFile::InitWithFile(FLAGS_bitstream_file); + if (!bitstream_file) { + std::cerr << "Can't open base bitstream file: " + << FLAGS_bitstream_file << std::endl; + return 1; + } + + auto bitstream_reader = xc7series::BitstreamReader::InitWithBytes( + bitstream_file->as_bytes()); + if (!bitstream_reader) { + std::cout + << "Bitstream does not appear to be a 7-series bitstream!" + << std::endl; + return 1; + } + + auto bitstream_config = + xc7series::Configuration::InitWithPackets(*part, *bitstream_reader); + if (!bitstream_config) { + std::cerr << "Bitstream does not appear to be for this part" + << std::endl; + return 1; + } + + // Copy the base frames to a mutable collection + std::map> frames; + for (auto& frame_val : bitstream_config->frames()) { + auto& cur_frame = frames[frame_val.first]; + + std::copy(frame_val.second.begin(), frame_val.second.end(), + std::back_inserter(cur_frame)); + } + + // Apply the deltas. + std::ifstream frm_file(FLAGS_frm_file); + if (!frm_file) { + std::cerr << "Unable to open frm file: " << FLAGS_frm_file + << std::endl; + return 1; + } + + std::string frm_line; + while (std::getline(frm_file, frm_line)) { + if (frm_line[0] == '#') + continue; + + std::pair frame_delta = + absl::StrSplit(frm_line, ' '); + + uint32_t frame_address = + std::stoul(frame_delta.first, nullptr, 16); + + auto& frame_data = frames[frame_address]; + frame_data.resize(101); + + std::vector frame_data_strings = + absl::StrSplit(frame_delta.second, ','); + if (frame_data_strings.size() != 101) { + std::cerr << "Frame " << std::hex << frame_address + << ": found " << std::dec + << frame_data_strings.size() + << "words instead of 101"; + continue; + }; + + std::transform(frame_data_strings.begin(), + frame_data_strings.end(), frame_data.begin(), + [](const std::string& val) -> uint32_t { + return std::stoul(val, nullptr, 16); + }); + + uint32_t ecc = 0; + for (size_t ii = 0; ii < frame_data.size(); ++ii) { + uint32_t word = frame_data[ii]; + uint32_t offset = ii * 32; + if (ii > 0x25) { + offset += 0x1360; + } else if (ii > 0x6) { + offset += 0x1340; + } else { + offset += 0x1320; + } + + // Mask out where the ECC should be. + if (ii == 0x32) { + word &= 0xFFFFE000; + } + + for (int jj = 0; jj < 32; ++jj) { + if ((word & 1) == 1) { + ecc ^= offset + jj; + } + word >>= 1; + } + } + + uint32_t v = ecc & 0xFFF; + v ^= v >> 8; + v ^= v >> 4; + v ^= v >> 2; + v ^= v >> 1; + ecc ^= (v & 1) << 12; + + // Replace the old ECC with the new. + frame_data[0x32] &= 0xFFFFE000; + frame_data[0x32] |= (ecc & 0x1FFF); + } + +#if 0 + for (auto& frame : frames) { + std::cout << "0x" << std::hex + << static_cast(frame.first) << " "; + + for (auto& word : frame.second) { + std::cout << "0x" << std::hex << word << ","; + } + + std::cout << std::endl; + } +#endif + std::vector out_packets; + + // Generate a single type 2 packet that writes everything at once. + std::vector packet_data; + for (auto& frame : frames) { + std::copy(frame.second.begin(), frame.second.end(), + std::back_inserter(packet_data)); + + auto next_address = part->GetNextFrameAddress(frame.first); + if (next_address && + (next_address->block_type() != frame.first.block_type() || + next_address->is_bottom_half_rows() != + frame.first.is_bottom_half_rows() || + next_address->row() != frame.first.row())) { + packet_data.insert(packet_data.end(), 202, 0); + } + } + packet_data.insert(packet_data.end(), 202, 0); + + out_packets.push_back(xc7series::ConfigurationPacket( + 1, xc7series::ConfigurationPacket::Opcode::Write, + xc7series::ConfigurationRegister::FDRI, {})); + out_packets.push_back(xc7series::ConfigurationPacket( + 2, xc7series::ConfigurationPacket::Opcode::Write, + xc7series::ConfigurationRegister::FDRI, packet_data)); + +#if 0 + for (auto& packet : out_packets) { + std::cout << packet << std::endl; + } +#endif + + // Write bitstream. + xc7series::BitstreamWriter out_bitstream_writer(out_packets); + std::ofstream out_file(FLAGS_output_file); + if (!out_file) { + std::cerr << "Unable to open file for writting: " + << FLAGS_output_file << std::endl; + return 1; + } + + for (uint32_t word : out_bitstream_writer) { + out_file.put((word >> 24) & 0xFF); + out_file.put((word >> 16) & 0xFF); + out_file.put((word >> 8) & 0xFF); + out_file.put((word)&0xFF); + } + + return 0; +}