├── README.md ├── analyzer ├── Makefile └── analyzer.cc ├── demos ├── ice8k │ ├── icedemo.pcf │ ├── icedemo.v │ ├── icestorm.sh │ ├── testbench.sh │ └── testbench.v └── zybo │ ├── zybo.v │ ├── zybo.xdc │ ├── zybo_prog.tcl │ └── zybo_synth.tcl ├── plinksrc ├── bitdist.py ├── ponylink_8b10b.v ├── ponylink_crc32.v ├── ponylink_master.v ├── ponylink_pack.v ├── ponylink_slave.v ├── ponylink_test.v ├── ponylink_txrx.v ├── protocol.txt ├── synth_icecube.sdc ├── synth_vivado.tcl ├── synth_vivado.xdc ├── synth_yosys.ys └── timings.py ├── smt2bmc ├── README ├── chip.py ├── chip.sh ├── chip.ys ├── reset_sequence.py ├── reset_sequence.sh ├── reset_sequence.v ├── reset_sequence.ys ├── slave_send_length.py ├── slave_send_length.sh ├── slave_send_length.v ├── slave_send_length.ys └── smtio.py └── testbench ├── Makefile └── maketest.py /README.md: -------------------------------------------------------------------------------- 1 | 2 | PonyLink -- A single-wire bi-directional chip-to-chip interface for FPGAs 3 | ========================================================================= 4 | 5 | PonyLink is a bi-directional chip-to-chip interface that is using only a single 6 | signal wire between the two chips. Naturally this wire is used in a half-duplex 7 | fashion. For faster link speeds the use of a LVDS pair is recommended. The 8 | cores are tested on Xilinx Series 7 and Lattice iCE40 FPGAs. 9 | 10 | On the chip-facing side PonyLink provides a transmit and receive AXI Stream as 11 | well as 8 GPIO inputs and 8 GPIO outputs. PonyLink handles all the low-level 12 | tasks, including flow control and detection of failed transfers and automatic 13 | resend. 14 | 15 | The main features of the core are that it only requires one single data line 16 | between the chips and that it can operate on resonable data rates (compared to 17 | the clock rates of the clocks driving the cores, usually over 0.5 MBit/s per 18 | MHz). This comes at a high resource cost, e.g. on iCE40 FPGAs (4-input LUTs) 19 | an instance of PonyLink uses about 1700 LUTs. 20 | 21 | If you can afford more than one signal line, or don't need the high bandwidth, 22 | then the resource cost for this core might be a bit high compared to possible 23 | other solutions. 24 | 25 | Features: 26 | --------- 27 | 28 | - typical net data rates of over 100 MBit/s at 166 MHz 29 | - can utilize up to 4x serdes hardware for higher link speed 30 | - support for any AXIS TDATA and TUSER width and TLAST signal 31 | - bi-directional communication over a single data line (usually LVDS) 32 | - works without a dedicated hardware block for clock recovery 33 | - dc-free signaling, allowing for caps or magnetics in the link 34 | - embedded clock and control signals (using 8b10b encoding) 35 | - support for different data rates for each direction 36 | - 8 asynchonous GPIO pins in each direction 37 | - spread EMI spectrum via data scrambling 38 | 39 | License: 40 | -------- 41 | 42 | The PonyLink core is licensed using permissive licenses. Most of it 43 | is covered by the ISC license. The [8b10b encoder and decoder](plinksrc/ponylink_8b10b.v) 44 | used in the project and the [CRC32 generator](plinksrc/ponylink_crc32.v) 45 | are licensed under different, but compatible permissive licenses. 46 | 47 | Master and Slave Roles and Link Reset: 48 | -------------------------------------- 49 | 50 | - One of the two PonyLink IPs must run in MASTER and one in SLAVE mode 51 | - The MASTER IP has a reset input signal 52 | - Resetting the MASTER will reset the SLAVE over the link 53 | - The SLAVE IP has a reset output that is pulsed by a reset event 54 | 55 | Block Diagram: 56 | -------------- 57 | 58 | +--------------------------------------------------+ 59 | | Application Logic on Chip A | 60 | +--------------------------------------------------+ 61 | ^ | ^ 62 | | | | 63 | v v | \ 64 | +------+ +----------------+ +----------------+ | 65 | | GPIO | | AXIS Input | | AXIS Output | | 66 | +------+ +----------------+ +----------------+ | 67 | ^ | ^ | 68 | | v | | 69 | | +----------------+ +----------------+ | 70 | | | Pack | | Unpack | | 71 | | +----------------+ +----------------+ | 72 | | | ^ | PonyLink 73 | | v | | Master 74 | | +----------------+ +----------------+ | on Chip A 75 | | | Scramble | | Unscramble | | 76 | | +----------------+ +----------------+ | 77 | | | ^ | 78 | | v | | 79 | | +--------------------------------------+ | 80 | +------>| TX/RX Engine | | 81 | +--------------------------------------+ | 82 | ^ / 83 | | 84 | | 85 | | 86 | v \ 87 | +--------------------------------------+ | 88 | +------>| TX/RX Engine | | 89 | | +--------------------------------------+ | 90 | | | ^ | 91 | | v | | 92 | | +----------------+ +----------------+ | 93 | | | Unscramble | | Scramble | | 94 | | +----------------+ +----------------+ | 95 | | | ^ | PonyLink 96 | | v | | Slave 97 | | +----------------+ +----------------+ | on Chip B 98 | | | Unpack | | Pack | | 99 | | +----------------+ +----------------+ | 100 | | | ^ | 101 | v v | | 102 | +------+ +----------------+ +----------------+ | 103 | | GPIO | | AXIS Output | | AXIS Input | | 104 | +------+ +----------------+ +----------------+ | 105 | ^ | ^ / 106 | | | | 107 | v v | 108 | +--------------------------------------------------+ 109 | | Application Logic on Chip B | 110 | +--------------------------------------------------+ 111 | 112 | See [plinksrc/protocol.txt](plinksrc/protocol.txt) for more details. 113 | 114 | 115 | Files in this repository 116 | ======================== 117 | 118 | [plinksrc/](plinksrc/): 119 | ----------------------- 120 | 121 | The actual PonyLink source. In most use cases you simply want to copy this 122 | directory into your project. 123 | 124 | [demos/ice8k/](demos/ice8k/): 125 | ----------------------------- 126 | 127 | A simple demo for PonyLink running on the Lattice iCE40 HX8K Breakout Board. 128 | This example is built using the FOSS Lattice iCE40 toolchain from [Project 129 | IceStorm](http://www.clifford.at/icestorm/). 130 | 131 | [demos/zybo/](demos/zybo/): 132 | --------------------------- 133 | 134 | A simple demo for PonyLink running on the Digilent Zybo Board (featuring a Zynq 135 | FPGA). This example is built using Xilinx Vivado. 136 | 137 | [testbench/](testbench/): 138 | ------------------------- 139 | 140 | A testbench (or rather testbench generator) for PonyLink. Running `make` in 141 | this directory will create 100 test benches and run them. 142 | 143 | [analyzer/](analyzer/): 144 | ----------------------- 145 | 146 | A low-level analyzer for PonyLink traffic. Reads a text file with one floating 147 | point number (representing a sample) per line. Can be used to analyze PonyLink 148 | traffic recorded with an oszilloscope. 149 | 150 | [smt2bmc/](smt2bmc/): 151 | --------------------- 152 | 153 | Some formal proofs regarding PonyLink, based on elements from the Yosys-SMTBMC 154 | flow. 155 | 156 | 157 | Howto use PonyLink in your designs 158 | ================================== 159 | 160 | PonyLink uses a master/slave architecture. Note that the slave core is reset 161 | via the link and has a `resetn_out` output port that can be used to trigger 162 | a reset of other circuits implemented on the slave side of the link. 163 | 164 | Instantiate `ponylink_master` in the FPGA design that sits on the master side 165 | of the link, and instantiate `ponylink_slave` on the slave side. 166 | 167 | Make sure that the parameters `M2S_TDATA_WIDTH`, `M2S_TUSER_WIDTH`, 168 | `S2M_TDATA_WIDTH`, and `S2M_TUSER_WIDTH` have the same values on both sides. 169 | 170 | When a serdes core is used, set `MASTER_PARBITS` and `SLAVE_PARBITS` to the 171 | correct serdes width. 172 | 173 | Use the `timings.py` script to generate the values for the `MASTER_TIMINGS` and 174 | `SLAVE_TIMINGS` parameter. The 1st and 2nd parameters to the script specify the 175 | clock period on the slave and master side in ns. When a serdes core is used, this 176 | is the period for the fast side of the serdes. The 3rd and 4th parameter specify 177 | the maximum expected edge-to-edge jitter for pulses transmitted from the master 178 | to the slave and vice versa respectively. Increase the values for the 3rd and 4th 179 | parameters if transmission errors are detected. 180 | 181 | Note that PonyLink performs best if the master and slave clock rates are *not* 182 | integer multiples of each other. 183 | 184 | 185 | Why the obscure name? 186 | ===================== 187 | 188 | All other "YadaYada-Link" names seem to be already taken. 189 | 190 | -------------------------------------------------------------------------------- /analyzer/Makefile: -------------------------------------------------------------------------------- 1 | CC = clang 2 | CXX = clang 3 | CFLAGS = -Wall 4 | CXXFLAGS = -Wall -std=c++11 5 | LDLIBS = -lstdc++ 6 | analyzer: analyzer.o 7 | -------------------------------------------------------------------------------- /analyzer/analyzer.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct PonyAnalyzer 6 | { 7 | struct message_t { 8 | size_t start_index, timing_cfg; 9 | float base_width; 10 | }; 11 | 12 | std::vector messages; 13 | std::vector samples; 14 | std::vector pulses; 15 | std::vector> timing_configs; 16 | float max_sample, min_sample, trigger; 17 | 18 | void read_samples(const char *filename) 19 | { 20 | std::ifstream f; 21 | f.open(filename); 22 | if (f.fail()) { 23 | std::cerr << "Can't open input file '" << filename << "'!" << std::endl; 24 | exit(1); 25 | } 26 | 27 | float sample; 28 | while (f >> sample) { 29 | if (samples.empty()) { 30 | max_sample = sample; 31 | min_sample = sample; 32 | } else { 33 | max_sample = std::max(sample, max_sample); 34 | min_sample = std::min(sample, min_sample); 35 | } 36 | samples.push_back(sample); 37 | trigger = (max_sample + min_sample) * 0.5; 38 | } 39 | } 40 | 41 | void extract_pulses() 42 | { 43 | pulses.clear(); 44 | pulses.push_back(0); 45 | 46 | for (float sample : samples) { 47 | if (sample < trigger) { 48 | if (pulses.size() % 2 == 0) 49 | pulses.push_back(0); 50 | } else { 51 | if (pulses.size() % 2 == 1) 52 | pulses.push_back(0); 53 | } 54 | pulses.back()++; 55 | } 56 | } 57 | 58 | bool about_equal(float a, float b) 59 | { 60 | if (a*0.9 > b) return false; 61 | if (a < b*0.9) return false; 62 | return true; 63 | } 64 | 65 | void find_messages() 66 | { 67 | std::vector init_sequence = {0, 0, 0, 0, 2, 4, 2, 0, 0, 0, 0, 0, 1, 0, 0}; 68 | 69 | for (size_t i = 0, pos = 0; i+20 < pulses.size(); pos += pulses[i], i++) 70 | for (size_t k = 0; k < timing_configs.size(); k++) 71 | { 72 | float base_width = ((pulses[i] + pulses[i+1] + pulses[i+2] + pulses[i+3]) / 4.0) / timing_configs[k][0]; 73 | for (size_t n = 0; n < init_sequence.size(); n++) 74 | if (!about_equal(timing_configs[k][init_sequence[n]]*base_width, pulses[i+n])) 75 | goto next_sample; 76 | 77 | std::cout << "Message #" << messages.size() << ": Start at sample " << pos << " (pulse " << i << 78 | ") with timing profile " << k << " and base width " << base_width << "." << std::endl; 79 | 80 | message_t msg; 81 | msg.start_index = i; 82 | msg.timing_cfg = k; 83 | msg.base_width = base_width; 84 | messages.push_back(msg); 85 | next_sample:; 86 | } 87 | } 88 | 89 | std::string decode_8b10b(uint32_t symbol) 90 | { 91 | std::string symbol_name; 92 | int val_upper = -1, val_lower = -1; 93 | 94 | bool a = (symbol & (1 << 9)) != 0; 95 | bool b = (symbol & (1 << 8)) != 0; 96 | bool c = (symbol & (1 << 7)) != 0; 97 | bool d = (symbol & (1 << 6)) != 0; 98 | bool e = (symbol & (1 << 5)) != 0; 99 | bool i = (symbol & (1 << 4)) != 0; 100 | bool f = (symbol & (1 << 3)) != 0; 101 | bool g = (symbol & (1 << 2)) != 0; 102 | bool h = (symbol & (1 << 1)) != 0; 103 | bool j = (symbol & (1 << 0)) != 0; 104 | 105 | std::string abcdei = std::string(a ? "1" : "0") + std::string(b ? "1" : "0") + std::string(c ? "1" : "0") + 106 | std::string(d ? "1" : "0") + std::string(e ? "1" : "0") + std::string(i ? "1" : "0"); 107 | 108 | std::string fghj = std::string(f ? "1" : "0") + std::string(g ? "1" : "0") + std::string(h ? "1" : "0") + std::string(j ? "1" : "0"); 109 | 110 | if (abcdei == "100111" || abcdei == "011000") symbol_name = "D.00", val_lower = 0; 111 | if (abcdei == "011101" || abcdei == "100010") symbol_name = "D.01", val_lower = 1; 112 | if (abcdei == "101101" || abcdei == "010010") symbol_name = "D.02", val_lower = 2; 113 | if (abcdei == "110001" ) symbol_name = "D.03", val_lower = 3; 114 | if (abcdei == "110101" || abcdei == "001010") symbol_name = "D.04", val_lower = 4; 115 | if (abcdei == "101001" ) symbol_name = "D.05", val_lower = 5; 116 | if (abcdei == "011001" ) symbol_name = "D.06", val_lower = 6; 117 | if (abcdei == "111000" || abcdei == "000111") symbol_name = "D.07", val_lower = 7; 118 | if (abcdei == "111001" || abcdei == "000110") symbol_name = "D.08", val_lower = 8; 119 | if (abcdei == "100101" ) symbol_name = "D.09", val_lower = 9; 120 | if (abcdei == "010101" ) symbol_name = "D.10", val_lower = 10; 121 | if (abcdei == "110100" ) symbol_name = "D.11", val_lower = 11; 122 | if (abcdei == "001101" ) symbol_name = "D.12", val_lower = 12; 123 | if (abcdei == "101100" ) symbol_name = "D.13", val_lower = 13; 124 | if (abcdei == "011100" ) symbol_name = "D.14", val_lower = 14; 125 | if (abcdei == "010111" || abcdei == "101000") symbol_name = "D.15", val_lower = 15; 126 | if (abcdei == "011011" || abcdei == "100100") symbol_name = "D.16", val_lower = 16; 127 | if (abcdei == "100011" ) symbol_name = "D.17", val_lower = 17; 128 | if (abcdei == "010011" ) symbol_name = "D.18", val_lower = 18; 129 | if (abcdei == "110010" ) symbol_name = "D.19", val_lower = 19; 130 | if (abcdei == "001011" ) symbol_name = "D.20", val_lower = 20; 131 | if (abcdei == "101010" ) symbol_name = "D.21", val_lower = 21; 132 | if (abcdei == "011010" ) symbol_name = "D.22", val_lower = 22; 133 | if (abcdei == "111010" || abcdei == "000101") symbol_name = "D.23", val_lower = 23; 134 | if (abcdei == "110011" || abcdei == "001100") symbol_name = "D.24", val_lower = 24; 135 | if (abcdei == "100110" ) symbol_name = "D.25", val_lower = 25; 136 | if (abcdei == "010110" ) symbol_name = "D.26", val_lower = 26; 137 | if (abcdei == "110110" || abcdei == "001001") symbol_name = "D.27", val_lower = 27; 138 | if (abcdei == "001110" ) symbol_name = "D.28", val_lower = 28; 139 | if (abcdei == "001111" || abcdei == "110000") symbol_name = "K.28", val_lower = 28; 140 | if (abcdei == "101110" || abcdei == "010001") symbol_name = "D.29", val_lower = 29; 141 | if (abcdei == "011110" || abcdei == "100001") symbol_name = "D.30", val_lower = 30; 142 | if (abcdei == "101011" || abcdei == "010100") symbol_name = "D.31", val_lower = 31; 143 | 144 | if (symbol_name[0] == 'D') { 145 | if (fghj == "1011" || fghj == "0100") symbol_name += ".0 ", val_upper = 0; 146 | if (fghj == "1001" ) symbol_name += ".1 ", val_upper = 1; 147 | if (fghj == "0101" ) symbol_name += ".2 ", val_upper = 2; 148 | if (fghj == "1100" || fghj == "0011") symbol_name += ".3 ", val_upper = 3; 149 | if (fghj == "1101" || fghj == "0010") symbol_name += ".4 ", val_upper = 4; 150 | if (fghj == "1010" ) symbol_name += ".5 ", val_upper = 5; 151 | if (fghj == "0110" ) symbol_name += ".6 ", val_upper = 6; 152 | if (fghj == "1110" || fghj == "0001") symbol_name += ".P7", val_upper = 7; 153 | if (fghj == "0111" || fghj == "1000") symbol_name += ".A7", val_upper = 7; 154 | } 155 | 156 | if (symbol_name[0] == 'K') { 157 | if (fghj == "1011" || fghj == "0100") symbol_name += ".0 ", val_upper = 0 + 8; 158 | if (fghj == "0110" || fghj == "1001") symbol_name += ".1 ", val_upper = 1 + 8; 159 | if (fghj == "1010" || fghj == "0101") symbol_name += ".2 ", val_upper = 2 + 8; 160 | if (fghj == "1100" || fghj == "0011") symbol_name += ".3 ", val_upper = 3 + 8; 161 | if (fghj == "1101" || fghj == "0010") symbol_name += ".4 ", val_upper = 4 + 8; 162 | if (fghj == "0101" || fghj == "1010") symbol_name += ".5 ", val_upper = 5 + 8; 163 | if (fghj == "1001" || fghj == "0110") symbol_name += ".6 ", val_upper = 6 + 8; 164 | if (fghj == "0111" || fghj == "1000") symbol_name += ".7 ", val_upper = 7 + 8; 165 | } 166 | 167 | if (symbol_name == "K.28.2 .5 ") { 168 | if (abcdei+fghj == "0011110101" || abcdei+fghj == "1100001010") symbol_name = "K.28.2 ", val_upper = 2 + 8; 169 | if (abcdei+fghj == "0011111010" || abcdei+fghj == "1100000101") symbol_name = "K.28.5 ", val_upper = 5 + 8; 170 | } 171 | 172 | int value = (val_upper << 5) | val_lower; 173 | char buffer[100]; 174 | 175 | snprintf(buffer, 100, " %3d %3x", value, value); 176 | symbol_name += buffer; 177 | 178 | return symbol_name; 179 | } 180 | 181 | void decode_messages() 182 | { 183 | size_t message_idx = 0; 184 | std::vector tmconfig; 185 | float base_width; 186 | bool active = false; 187 | bool waiting = false; 188 | 189 | uint32_t buffer; 190 | int buffer_n = 0; 191 | 192 | for (size_t i = 0; i < pulses.size(); i++) 193 | { 194 | if (message_idx < messages.size() && i == messages[message_idx].start_index) { 195 | std::cout << "Message #" << message_idx << ":" << std::endl << 196 | " Using timing profile " << messages[message_idx].timing_cfg << 197 | " and base width " << messages[message_idx].base_width << "." << std::endl; 198 | tmconfig = timing_configs[messages[message_idx].timing_cfg]; 199 | base_width = messages[message_idx].base_width; 200 | message_idx++; 201 | waiting = true; 202 | continue; 203 | } 204 | 205 | if (!active && !waiting) 206 | continue; 207 | 208 | for (size_t k = 0; k < tmconfig.size(); k++) 209 | if (about_equal(tmconfig[k] * base_width, pulses[i])) { 210 | for (int l = 0; l < k+1; l++) { 211 | buffer = buffer << 1 | (i % 2); 212 | buffer_n++; 213 | } 214 | goto matched_pulse; 215 | } 216 | 217 | std::cout << " END-OF-MESSAGE" << std::endl; 218 | active = false; 219 | waiting = false; 220 | continue; 221 | 222 | matched_pulse:; 223 | if (waiting && buffer_n >= 10 && (buffer & 0x7f) == 0x1f) { 224 | active = true; 225 | waiting = false; 226 | buffer_n = 7; 227 | } 228 | 229 | if (active && buffer_n >= 10) { 230 | std::cout << " "; 231 | uint32_t symbol = 0; 232 | for (int k = 0; k < 10; k++) { 233 | int bit = (buffer >> --buffer_n) & 1; 234 | std::cout << (bit ? '1' : '0'); 235 | symbol = (symbol << 1) | bit; 236 | } 237 | std::cout << " " << decode_8b10b(symbol) << std::endl; 238 | } 239 | } 240 | } 241 | }; 242 | 243 | int main() 244 | { 245 | PonyAnalyzer pa; 246 | pa.read_samples("waveform.txt"); 247 | 248 | std::cout << "Read " << pa.samples.size() << " samples from waveform.txt." << std::endl; 249 | std::cout << "Sample range: " << pa.min_sample << " .. " << pa.max_sample << std::endl; 250 | std::cout << "Trigger level: " << pa.trigger << std::endl; 251 | 252 | pa.extract_pulses(); 253 | std::cout << "Extracted " << pa.pulses.size() << " pulses." << std::endl; 254 | #if 0 255 | for (size_t i = 0, pos = 0; i < pa.pulses.size(); pos += pa.pulses[i], i++) 256 | std::cout << " " << pos << " [" << i << "]: " << pa.pulses[i] << std::endl; 257 | #endif 258 | 259 | // Configure timing profiles 260 | pa.timing_configs.push_back(std::vector({1, 2, 3, 4, 5})); 261 | 262 | pa.find_messages(); 263 | pa.decode_messages(); 264 | 265 | return 0; 266 | } 267 | 268 | -------------------------------------------------------------------------------- /demos/ice8k/icedemo.pcf: -------------------------------------------------------------------------------- 1 | set_io led_0 B5 2 | set_io led_1 B4 3 | set_io led_2 A2 4 | set_io led_3 A1 5 | set_io led_4 C5 6 | set_io led_5 C4 7 | set_io led_6 B3 8 | set_io led_7 C3 9 | 10 | # on-board oscillator (12.0 MHz) 11 | set_io clk_pin J3 12 | 13 | set_io master_io F15 14 | set_io slave_io G15 15 | 16 | set_io master_en T1 17 | set_io master_out R2 18 | set_io master_in R3 19 | 20 | set_io master_rst T2 21 | set_io master_clk R4 22 | 23 | set_io slave_en B1 24 | set_io slave_out C1 25 | set_io slave_in D1 26 | 27 | set_io slave_rst B2 28 | set_io slave_clk D2 29 | 30 | -------------------------------------------------------------------------------- /demos/ice8k/icedemo.v: -------------------------------------------------------------------------------- 1 | `timescale 1 ns / 1 ps 2 | 3 | module icedemo ( 4 | input clk_pin, 5 | 6 | inout master_io, 7 | output master_en, 8 | output master_in, 9 | output master_out, 10 | output master_rst, 11 | output master_clk, 12 | 13 | inout slave_io, 14 | output slave_en, 15 | output slave_in, 16 | output slave_out, 17 | output slave_rst, 18 | output slave_clk, 19 | 20 | output led_0, 21 | output led_1, 22 | output led_2, 23 | output led_3, 24 | output led_4, 25 | output led_5, 26 | output led_6, 27 | output led_7 28 | ); 29 | localparam MASTER_TIMINGS = 80'h0e0b0805020d0a070401; 30 | localparam SLAVE_TIMINGS = 80'h0e0b0805020d0a070401; 31 | 32 | // --------------------------- 33 | // PonyLink Master 34 | 35 | wire master_clk; 36 | wire master_resetn; 37 | 38 | wire master_linkerror; 39 | wire master_linkready; 40 | 41 | wire master_mode_recv; 42 | wire master_mode_send; 43 | 44 | wire [7:0] master_gpio_i; 45 | wire [7:0] master_gpio_o; 46 | 47 | wire [15:0] master_in_tdata; 48 | wire master_in_tvalid; 49 | wire master_in_tlast; 50 | wire master_in_tready; 51 | 52 | wire [15:0] master_out_tdata; 53 | wire master_out_tvalid; 54 | wire master_out_tlast; 55 | wire master_out_tready; 56 | 57 | wire master_serdes_in; 58 | wire master_serdes_out; 59 | wire master_serdes_en; 60 | 61 | ponylink_master #( 62 | .MASTER_PARBITS(1), 63 | .SLAVE_PARBITS(1), 64 | .M2S_TDATA_WIDTH(16), 65 | .S2M_TDATA_WIDTH(16), 66 | .MASTER_TIMINGS(MASTER_TIMINGS), 67 | .SLAVE_TIMINGS(SLAVE_TIMINGS) 68 | ) master_core ( 69 | .clk (master_clk ), 70 | .resetn (master_resetn ), 71 | .linkerror (master_linkerror ), 72 | .linkready (master_linkready ), 73 | .mode_recv (master_mode_recv ), 74 | .mode_send (master_mode_send ), 75 | .gpio_i (master_gpio_i ), 76 | .gpio_o (master_gpio_o ), 77 | .in_tdata (master_in_tdata ), 78 | .in_tvalid (master_in_tvalid ), 79 | .in_tlast (master_in_tlast ), 80 | .in_tready (master_in_tready ), 81 | .out_tdata (master_out_tdata ), 82 | .out_tvalid(master_out_tvalid), 83 | .out_tlast (master_out_tlast ), 84 | .out_tready(master_out_tready), 85 | .serdes_in (master_serdes_in ), 86 | .serdes_out(master_serdes_out), 87 | .serdes_en (master_serdes_en ) 88 | ); 89 | 90 | 91 | // --------------------------- 92 | // PonyLink Slave 93 | 94 | wire slave_clk; 95 | wire slave_resetn; 96 | 97 | wire slave_linkerror; 98 | wire slave_linkready; 99 | 100 | wire slave_mode_recv; 101 | wire slave_mode_send; 102 | 103 | wire [7:0] slave_gpio_i; 104 | wire [7:0] slave_gpio_o; 105 | 106 | wire [15:0] slave_in_tdata; 107 | wire slave_in_tvalid; 108 | wire slave_in_tlast; 109 | wire slave_in_tready; 110 | 111 | wire [15:0] slave_out_tdata; 112 | wire slave_out_tvalid; 113 | wire slave_out_tlast; 114 | wire slave_out_tready; 115 | 116 | wire slave_serdes_in; 117 | wire slave_serdes_out; 118 | wire slave_serdes_en; 119 | 120 | ponylink_slave #( 121 | .MASTER_PARBITS(1), 122 | .SLAVE_PARBITS(1), 123 | .M2S_TDATA_WIDTH(16), 124 | .S2M_TDATA_WIDTH(16), 125 | .MASTER_TIMINGS(MASTER_TIMINGS), 126 | .SLAVE_TIMINGS(SLAVE_TIMINGS) 127 | ) slave_core ( 128 | .clk (slave_clk ), 129 | .resetn_out(slave_resetn ), 130 | .linkerror (slave_linkerror ), 131 | .linkready (slave_linkready ), 132 | .mode_recv (slave_mode_recv ), 133 | .mode_send (slave_mode_send ), 134 | .gpio_i (slave_gpio_i ), 135 | .gpio_o (slave_gpio_o ), 136 | .in_tdata (slave_in_tdata ), 137 | .in_tvalid (slave_in_tvalid ), 138 | .in_tlast (slave_in_tlast ), 139 | .in_tready (slave_in_tready ), 140 | .out_tdata (slave_out_tdata ), 141 | .out_tvalid(slave_out_tvalid), 142 | .out_tlast (slave_out_tlast ), 143 | .out_tready(slave_out_tready), 144 | .serdes_in (slave_serdes_in ), 145 | .serdes_out(slave_serdes_out), 146 | .serdes_en (slave_serdes_en ) 147 | ); 148 | 149 | 150 | // --------------------------- 151 | // Master Reset Generator 152 | 153 | reg [19:0] m_reset_counter = 0; 154 | reg m_resetn = 0; 155 | 156 | always @(posedge master_clk) begin 157 | if (&m_reset_counter) begin 158 | m_resetn <= 1; 159 | end else begin 160 | m_reset_counter <= m_reset_counter + 1; 161 | m_resetn <= 0; 162 | end 163 | end 164 | 165 | 166 | // --------------------------- 167 | // Master Sender 168 | 169 | reg [15:0] m_send_tdata = 0; 170 | wire m_send_tready; 171 | wire m_send_mode; 172 | 173 | always @(posedge master_clk) begin 174 | if (m_send_tready) begin 175 | `ifndef SIM 176 | if (m_send_mode) 177 | m_send_tdata <= m_send_tdata - 5; 178 | else 179 | m_send_tdata <= m_send_tdata + 2; 180 | `else 181 | if (m_send_mode) 182 | m_send_tdata <= m_send_tdata - 50; 183 | else 184 | m_send_tdata <= m_send_tdata + 20; 185 | `endif 186 | end 187 | end 188 | 189 | 190 | // --------------------------- 191 | // Slave Sender 192 | 193 | reg [15:0] s_send_tdata = 0; 194 | wire s_send_tready; 195 | wire s_send_mode; 196 | 197 | always @(posedge master_clk) begin 198 | if (s_send_tready) begin 199 | `ifndef SIM 200 | if (s_send_mode) 201 | s_send_tdata <= s_send_tdata - 7; 202 | else 203 | s_send_tdata <= s_send_tdata + 3; 204 | `else 205 | if (s_send_mode) 206 | s_send_tdata <= s_send_tdata - 70; 207 | else 208 | s_send_tdata <= s_send_tdata + 30; 209 | `endif 210 | end 211 | end 212 | 213 | 214 | // --------------------------- 215 | // Master Receiver 216 | 217 | wire [15:0] m_recv_tdata; 218 | wire m_recv_tvalid; 219 | reg m_recv_mode = 0; 220 | 221 | always @(posedge master_clk) begin 222 | if (m_recv_tvalid) begin 223 | if (m_recv_tdata < 16'h4000) m_recv_mode <= 0; 224 | if (m_recv_tdata >= 16'hc000) m_recv_mode <= 1; 225 | end 226 | end 227 | 228 | 229 | // --------------------------- 230 | // Slave Receiver 231 | 232 | wire [15:0] s_recv_tdata; 233 | wire s_recv_tvalid; 234 | reg s_recv_mode = 0; 235 | 236 | always @(posedge slave_clk) begin 237 | if (s_recv_tvalid) begin 238 | if (s_recv_tdata < 16'h4000) s_recv_mode <= 0; 239 | if (s_recv_tdata >= 16'hc000) s_recv_mode <= 1; 240 | end 241 | end 242 | 243 | 244 | // --------------------------- 245 | // Master Clock Generator 246 | 247 | // SB_GB_IO #( 248 | // .PIN_TYPE(6'b 0000_01) 249 | // ) clk_gb ( 250 | // .PACKAGE_PIN(clk_pin), 251 | // .GLOBAL_BUFFER_OUTPUT(master_clk) 252 | // ); 253 | assign master_clk = clk_pin; 254 | 255 | 256 | // --------------------------- 257 | // Slave Clock Generator 258 | 259 | assign slave_clk = master_clk; 260 | 261 | 262 | // --------------------------- 263 | // Master IO Pin 264 | 265 | `ifndef SIM 266 | SB_IO #( 267 | .PIN_TYPE(6'b 1010_01), 268 | .PULLUP(1'b 0) 269 | ) master_io_buffer ( 270 | .PACKAGE_PIN(master_io), 271 | .OUTPUT_ENABLE(master_serdes_en), 272 | .D_OUT_0(master_serdes_out), 273 | .D_IN_0(master_serdes_in) 274 | ); 275 | `else 276 | assign master_io = master_serdes_en ? master_serdes_out : 1'bz; 277 | assign master_serdes_in = master_io === 1'b1; 278 | `endif 279 | 280 | assign master_en = master_serdes_en; 281 | assign master_out = master_serdes_out; 282 | assign master_in = master_serdes_in; 283 | assign master_rst = !master_resetn; 284 | 285 | 286 | // --------------------------- 287 | // Slave IO Pin 288 | 289 | `ifndef SIM 290 | SB_IO #( 291 | .PIN_TYPE(6'b 1010_01), 292 | .PULLUP(1'b 0) 293 | ) slave_io_buffer ( 294 | .PACKAGE_PIN(slave_io), 295 | .OUTPUT_ENABLE(slave_serdes_en), 296 | .D_OUT_0(slave_serdes_out), 297 | .D_IN_0(slave_serdes_in) 298 | ); 299 | `else 300 | assign slave_io = slave_serdes_en ? slave_serdes_out : 1'bz; 301 | assign slave_serdes_in = slave_io === 1'b1; 302 | `endif 303 | 304 | assign slave_en = slave_serdes_en; 305 | assign slave_out = slave_serdes_out; 306 | assign slave_in = slave_serdes_in; 307 | assign slave_rst = !slave_resetn; 308 | 309 | 310 | // --------------------------- 311 | // Status LEDs 312 | 313 | assign led_0 = master_linkready; 314 | assign led_1 = slave_linkready; 315 | assign led_2 = m_recv_mode; 316 | assign led_3 = s_recv_mode; 317 | assign led_4 = 0; 318 | assign led_5 = 0; 319 | assign led_6 = master_linkerror; 320 | assign led_7 = slave_linkerror; 321 | 322 | 323 | // --------------------------- 324 | // Master Wiring 325 | 326 | assign master_resetn = m_resetn; 327 | 328 | assign master_gpio_i = {7'b0, m_recv_mode}; 329 | assign m_send_mode = master_gpio_o[0]; 330 | 331 | assign master_in_tdata = m_send_tdata; 332 | assign master_in_tvalid = 1'b1; 333 | assign master_in_tlast = 1'b0; 334 | assign m_send_tready = master_in_tready; 335 | 336 | assign m_recv_tdata = master_out_tdata; 337 | assign m_recv_tvalid = master_out_tvalid; 338 | assign master_out_tready = 1'b1; 339 | 340 | 341 | // --------------------------- 342 | // Slave Wiring 343 | 344 | assign slave_gpio_i = {7'b0, s_recv_mode}; 345 | assign s_send_mode = slave_gpio_o[0]; 346 | 347 | assign slave_in_tdata = s_send_tdata; 348 | assign slave_in_tvalid = 1'b1; 349 | assign slave_in_tlast = 1'b0; 350 | assign s_send_tready = slave_in_tready; 351 | 352 | assign s_recv_tdata = slave_out_tdata; 353 | assign s_recv_tvalid = slave_out_tvalid; 354 | assign slave_out_tready = 1'b1; 355 | endmodule 356 | -------------------------------------------------------------------------------- /demos/ice8k/icestorm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | yosys -v9 -p 'synth_ice40 -top icedemo -blif icedemo_out.blif' icedemo.v \ 4 | ../../plinksrc/ponylink_{8b10b,crc32,master,pack,slave,txrx}.v 5 | arachne-pnr -d 8k -o icedemo_out.txt -p icedemo.pcf icedemo_out.blif 6 | icepack icedemo_out.txt icedemo_out.bin 7 | -------------------------------------------------------------------------------- /demos/ice8k/testbench.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | iverilog -DSIM -o testbench.exe -s testbench icedemo.v testbench.v ../../plinksrc/*.v 4 | ./testbench.exe 5 | -------------------------------------------------------------------------------- /demos/ice8k/testbench.v: -------------------------------------------------------------------------------- 1 | `timescale 1 ns / 1 ps 2 | 3 | module testbench; 4 | reg clk_pin = 1; 5 | always #500 clk_pin = ~clk_pin; 6 | 7 | wire master_io; 8 | wire master_en; 9 | 10 | wire slave_io; 11 | wire slave_en; 12 | 13 | wire led_0; 14 | wire led_1; 15 | wire led_2; 16 | wire led_3; 17 | wire led_4; 18 | wire led_5; 19 | wire led_6; 20 | wire led_7; 21 | 22 | icedemo uut ( 23 | .clk_pin (clk_pin ), 24 | .master_io(master_io), 25 | .master_en(master_en), 26 | .slave_io (slave_io ), 27 | .slave_en (slave_en ), 28 | .led_0 (led_0 ), 29 | .led_1 (led_1 ), 30 | .led_2 (led_2 ), 31 | .led_3 (led_3 ), 32 | .led_4 (led_4 ), 33 | .led_5 (led_5 ), 34 | .led_6 (led_6 ), 35 | .led_7 (led_7 ) 36 | ); 37 | 38 | tran (master_io, slave_io); 39 | 40 | wire [15:0] master_counter = uut.m_send_tdata; 41 | wire [15:0] slave_counter = uut.s_send_tdata; 42 | 43 | initial begin 44 | $dumpfile("testbench.vcd"); 45 | $dumpvars(1, testbench); 46 | @(negedge clk_pin); 47 | uut.m_reset_counter = -20; 48 | repeat (1000000) @(posedge clk_pin); 49 | $finish; 50 | end 51 | endmodule 52 | -------------------------------------------------------------------------------- /demos/zybo/zybo.v: -------------------------------------------------------------------------------- 1 | 2 | `timescale 1 ns / 1 ps 3 | 4 | module top ( 5 | // 125 MHz clock 6 | input iclk, 7 | `ifdef SYNTHESIS 8 | inout master_pin, 9 | inout slave_pin 10 | `else 11 | inout shared_pin 12 | `endif 13 | ); 14 | wire pll_feedback; 15 | wire pll_locked; 16 | wire clk, clk4; 17 | 18 | `ifdef SYNTHESIS 19 | wire clk125 = iclk; 20 | `else 21 | reg clk125 = 0; 22 | always @* clk125 <= #4 !clk125; 23 | `endif 24 | 25 | MMCME2_BASE #( 26 | .CLKIN1_PERIOD(8.0), 27 | .CLKFBOUT_MULT_F(8.0), 28 | .CLKOUT1_DIVIDE(20), 29 | .CLKOUT1_DUTY_CYCLE(0.5), 30 | .CLKOUT1_PHASE(0.0), 31 | .CLKOUT2_DIVIDE(5), 32 | .CLKOUT2_DUTY_CYCLE(0.5), 33 | .CLKOUT2_PHASE(0.0) 34 | ) pll ( 35 | .CLKIN1(clk125), 36 | .CLKOUT1(clk), 37 | .CLKOUT2(clk4), 38 | .CLKFBOUT(pll_feedback), 39 | .CLKFBIN(pll_feedback), 40 | .PWRDWN(1'b0), 41 | .LOCKED(pll_locked), 42 | .RST(1'b0) 43 | ); 44 | 45 | reg resetn; 46 | reg [3:0] locked_q; 47 | always @(posedge clk) 48 | {resetn, locked_q} <= {locked_q, pll_locked}; 49 | 50 | parameter M2S_TDATA_WIDTH = 8; 51 | parameter M2S_TUSER_WIDTH = 1; 52 | parameter S2M_TDATA_WIDTH = 8; 53 | parameter S2M_TUSER_WIDTH = 1; 54 | parameter MASTER_PARBITS = 4; 55 | parameter MASTER_TIMINGS = 80'h110c0805020f0a070401; 56 | parameter SLAVE_PARBITS = 4; 57 | parameter SLAVE_TIMINGS = 80'h110c0805020f0a070401; 58 | 59 | wire [7:0] master_gpio_i = 0; 60 | wire [7:0] master_gpio_o = 0; 61 | 62 | wire [7:0] slave_gpio_i = 0; 63 | wire [7:0] slave_gpio_o = 0; 64 | 65 | wire [M2S_TDATA_WIDTH-1:0] master_in_tdata = 0; 66 | wire [M2S_TUSER_WIDTH-1:0] master_in_tuser = 0; 67 | wire master_in_tvalid = 1; 68 | wire master_in_tlast = 0; 69 | wire master_in_tready; 70 | 71 | wire [S2M_TDATA_WIDTH-1:0] master_out_tdata; 72 | wire [S2M_TUSER_WIDTH-1:0] master_out_tuser; 73 | wire master_out_tvalid; 74 | wire master_out_tlast; 75 | wire master_out_tready = 1; 76 | 77 | wire [S2M_TDATA_WIDTH-1:0] slave_in_tdata = 0; 78 | wire [S2M_TUSER_WIDTH-1:0] slave_in_tuser = 0; 79 | wire slave_in_tvalid = 1; 80 | wire slave_in_tlast = 0; 81 | wire slave_in_tready; 82 | 83 | wire [M2S_TDATA_WIDTH-1:0] slave_out_tdata; 84 | wire [M2S_TUSER_WIDTH-1:0] slave_out_tuser; 85 | wire slave_out_tvalid; 86 | wire slave_out_tlast; 87 | wire slave_out_tready = 1; 88 | 89 | wire master_linkerror; 90 | wire slave_linkerror; 91 | wire slave_resetn; 92 | 93 | wire [MASTER_PARBITS-1:0] master_serdes_in; 94 | wire [MASTER_PARBITS-1:0] master_serdes_out; 95 | wire [MASTER_PARBITS-1:0] master_serdes_en; 96 | 97 | wire [SLAVE_PARBITS-1:0] slave_serdes_in; 98 | wire [SLAVE_PARBITS-1:0] slave_serdes_out; 99 | wire [SLAVE_PARBITS-1:0] slave_serdes_en; 100 | 101 | integer i; 102 | 103 | reg master_oserdes_last = 0; 104 | reg [3:0] master_oserdes_d; 105 | wire [3:0] master_iserdes_q; 106 | reg [2:0] master_oserdes_t; 107 | 108 | reg slave_oserdes_last = 0; 109 | reg [3:0] slave_oserdes_d; 110 | wire [3:0] slave_iserdes_q; 111 | reg [2:0] slave_oserdes_t; 112 | 113 | always @(posedge clk) begin 114 | master_oserdes_t <= {master_oserdes_t, 1'b1}; 115 | for (i = 0; i < 4; i = i+1) begin 116 | if (master_serdes_en[i]) begin 117 | master_oserdes_last = master_serdes_out[i]; 118 | master_oserdes_t[0] <= 0; 119 | end 120 | master_oserdes_d[i] <= master_oserdes_last; 121 | end 122 | end 123 | 124 | always @(posedge clk) begin 125 | slave_oserdes_t <= {slave_oserdes_t, 1'b1}; 126 | for (i = 0; i < 4; i = i+1) begin 127 | if (slave_serdes_en[i]) begin 128 | slave_oserdes_last = slave_serdes_out[i]; 129 | slave_oserdes_t[0] <= 0; 130 | end 131 | slave_oserdes_d[i] <= slave_oserdes_last; 132 | end 133 | end 134 | 135 | // assign master_serdes_in = (master_serdes_out & master_serdes_en) | (slave_serdes_out & ~master_serdes_en); 136 | // assign slave_serdes_in = (slave_serdes_out & slave_serdes_en) | (master_serdes_out & ~slave_serdes_en); 137 | 138 | `ifdef SYNTHESIS 139 | assign master_serdes_in = master_iserdes_q; 140 | assign slave_serdes_in = slave_iserdes_q; 141 | `else 142 | assign master_serdes_in = { 143 | master_iserdes_q[3] === 1'b1, 144 | master_iserdes_q[2] === 1'b1, 145 | master_iserdes_q[1] === 1'b1, 146 | master_iserdes_q[0] === 1'b1 147 | }; 148 | assign slave_serdes_in = { 149 | slave_iserdes_q[3] === 1'b1, 150 | slave_iserdes_q[2] === 1'b1, 151 | slave_iserdes_q[1] === 1'b1, 152 | slave_iserdes_q[0] === 1'b1 153 | }; 154 | `endif 155 | 156 | ponylink_master #( 157 | .M2S_TDATA_WIDTH(M2S_TDATA_WIDTH), 158 | .M2S_TUSER_WIDTH(M2S_TUSER_WIDTH), 159 | .S2M_TDATA_WIDTH(S2M_TDATA_WIDTH), 160 | .S2M_TUSER_WIDTH(S2M_TUSER_WIDTH), 161 | .MASTER_PARBITS(MASTER_PARBITS), 162 | .MASTER_TIMINGS(MASTER_TIMINGS), 163 | .SLAVE_PARBITS(SLAVE_PARBITS), 164 | .SLAVE_TIMINGS(SLAVE_TIMINGS) 165 | ) ponylink_master_core ( 166 | .clk(clk), 167 | .resetn(resetn), 168 | .linkerror(master_linkerror), 169 | 170 | .gpio_i(master_gpio_i), 171 | .gpio_o(master_gpio_o), 172 | 173 | .in_tdata(master_in_tdata), 174 | .in_tuser(master_in_tuser), 175 | .in_tlast(master_in_tlast), 176 | .in_tvalid(master_in_tvalid), 177 | .in_tready(master_in_tready), 178 | 179 | .out_tdata(master_out_tdata), 180 | .out_tuser(master_out_tuser), 181 | .out_tlast(master_out_tlast), 182 | .out_tvalid(master_out_tvalid), 183 | .out_tready(master_out_tready), 184 | 185 | .serdes_in(master_serdes_in), 186 | .serdes_out(master_serdes_out), 187 | .serdes_en(master_serdes_en) 188 | ); 189 | 190 | ponylink_slave #( 191 | .M2S_TDATA_WIDTH(M2S_TDATA_WIDTH), 192 | .M2S_TUSER_WIDTH(M2S_TUSER_WIDTH), 193 | .S2M_TDATA_WIDTH(S2M_TDATA_WIDTH), 194 | .S2M_TUSER_WIDTH(S2M_TUSER_WIDTH), 195 | .MASTER_PARBITS(MASTER_PARBITS), 196 | .MASTER_TIMINGS(MASTER_TIMINGS), 197 | .SLAVE_PARBITS(SLAVE_PARBITS), 198 | .SLAVE_TIMINGS(SLAVE_TIMINGS) 199 | ) ponylink_slave_core ( 200 | .clk(clk), 201 | .resetn_out(slave_resetn), 202 | .linkerror(slave_linkerror), 203 | 204 | .gpio_i(slave_gpio_i), 205 | .gpio_o(slave_gpio_o), 206 | 207 | .in_tdata(slave_in_tdata), 208 | .in_tuser(slave_in_tuser), 209 | .in_tlast(slave_in_tlast), 210 | .in_tvalid(slave_in_tvalid), 211 | .in_tready(slave_in_tready), 212 | 213 | .out_tdata(slave_out_tdata), 214 | .out_tuser(slave_out_tuser), 215 | .out_tlast(slave_out_tlast), 216 | .out_tvalid(slave_out_tvalid), 217 | .out_tready(slave_out_tready), 218 | 219 | .serdes_in(slave_serdes_in), 220 | .serdes_out(slave_serdes_out), 221 | .serdes_en(slave_serdes_en) 222 | ); 223 | 224 | wire master_oq, master_tq, master_d; 225 | wire slave_oq, slave_tq, slave_d; 226 | 227 | IOBUF master_buf ( 228 | .I(master_oq), 229 | .T(master_tq), 230 | .O(master_d), 231 | `ifdef SYNTHESIS 232 | .IO(master_pin) 233 | `else 234 | .IO(shared_pin) 235 | `endif 236 | ); 237 | 238 | IOBUF slave_buf ( 239 | .I(slave_oq), 240 | .T(slave_tq), 241 | .O(slave_d), 242 | `ifdef SYNTHESIS 243 | .IO(slave_pin) 244 | `else 245 | .IO(shared_pin) 246 | `endif 247 | ); 248 | 249 | OSERDESE2 #( 250 | .DATA_RATE_OQ("SDR"), 251 | .DATA_RATE_TQ("SDR"), 252 | .DATA_WIDTH(4), 253 | .INIT_OQ(1'b0), 254 | .INIT_TQ(1'b1), 255 | .SERDES_MODE("MASTER"), 256 | .SRVAL_OQ(1'b0), 257 | .SRVAL_TQ(1'b1), 258 | .TBYTE_CTL("FALSE"), 259 | .TBYTE_SRC("FALSE"), 260 | .TRISTATE_WIDTH(1) 261 | ) master_oserdes ( 262 | .OFB(), 263 | .OQ(master_oq), 264 | .SHIFTOUT1(), 265 | .SHIFTOUT2(), 266 | .TBYTEOUT(), 267 | .TFB(), 268 | .TQ(master_tq), 269 | .CLK(clk4), 270 | .CLKDIV(clk), 271 | .D1(master_oserdes_d[0]), 272 | .D2(master_oserdes_d[1]), 273 | .D3(master_oserdes_d[2]), 274 | .D4(master_oserdes_d[3]), 275 | .D5(), 276 | .D6(), 277 | .D7(), 278 | .D8(), 279 | .OCE(1'b1), 280 | .RST(~resetn), 281 | .SHIFTIN1(1'b0), 282 | .SHIFTIN2(1'b0), 283 | .T1(&master_oserdes_t), 284 | .T2(1'b0), 285 | .T3(1'b0), 286 | .T4(1'b0), 287 | .TBYTEIN(1'b0), 288 | .TCE(1'b1) 289 | ); 290 | 291 | OSERDESE2 #( 292 | .DATA_RATE_OQ("SDR"), 293 | .DATA_RATE_TQ("SDR"), 294 | .DATA_WIDTH(4), 295 | .INIT_OQ(1'b0), 296 | .INIT_TQ(1'b1), 297 | .SERDES_MODE("MASTER"), 298 | .SRVAL_OQ(1'b0), 299 | .SRVAL_TQ(1'b1), 300 | .TBYTE_CTL("FALSE"), 301 | .TBYTE_SRC("FALSE"), 302 | .TRISTATE_WIDTH(1) 303 | ) slave_oserdes ( 304 | .OFB(), 305 | .OQ(slave_oq), 306 | .SHIFTOUT1(), 307 | .SHIFTOUT2(), 308 | .TBYTEOUT(), 309 | .TFB(), 310 | .TQ(slave_tq), 311 | .CLK(clk4), 312 | .CLKDIV(clk), 313 | .D1(slave_oserdes_d[0]), 314 | .D2(slave_oserdes_d[1]), 315 | .D3(slave_oserdes_d[2]), 316 | .D4(slave_oserdes_d[3]), 317 | .D5(), 318 | .D6(), 319 | .D7(), 320 | .D8(), 321 | .OCE(1'b1), 322 | .RST(~resetn), 323 | .SHIFTIN1(1'b0), 324 | .SHIFTIN2(1'b0), 325 | .T1(&slave_oserdes_t), 326 | .T2(1'b0), 327 | .T3(1'b0), 328 | .T4(1'b0), 329 | .TBYTEIN(1'b0), 330 | .TCE(1'b1) 331 | ); 332 | 333 | ISERDESE2 #( 334 | .DATA_RATE("SDR"), 335 | .DATA_WIDTH(4), 336 | .DYN_CLKDIV_INV_EN("FALSE"), 337 | .DYN_CLK_INV_EN("FALSE"), 338 | .INIT_Q1(1'b0), 339 | .INIT_Q2(1'b0), 340 | .INIT_Q3(1'b0), 341 | .INIT_Q4(1'b0), 342 | .INTERFACE_TYPE("NETWORKING"), // MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE 343 | .IOBDELAY("NONE"), 344 | .NUM_CE(2), 345 | .OFB_USED("FALSE"), 346 | .SERDES_MODE("MASTER"), 347 | .SRVAL_Q1(1'b0), 348 | .SRVAL_Q2(1'b0), 349 | .SRVAL_Q3(1'b0), 350 | .SRVAL_Q4(1'b0) 351 | ) master_iserdes ( 352 | .O(), 353 | .Q1(master_iserdes_q[3]), 354 | .Q2(master_iserdes_q[2]), 355 | .Q3(master_iserdes_q[1]), 356 | .Q4(master_iserdes_q[0]), 357 | .Q5(), 358 | .Q6(), 359 | .Q7(), 360 | .Q8(), 361 | .SHIFTOUT1(), 362 | .SHIFTOUT2(), 363 | .BITSLIP(1'b0), 364 | .CE1(1'b1), 365 | .CE2(1'b1), 366 | .CLKDIVP(1'b0), 367 | .CLK(clk4), 368 | .CLKB(~clk4), 369 | .CLKDIV(clk), 370 | .OCLK(1'b0), 371 | .DYNCLKDIVSEL(1'b0), 372 | .DYNCLKSEL(1'b0), 373 | .D(master_d), 374 | .DDLY(1'b0), 375 | .OFB(1'b0), 376 | .OCLKB(1'b0), 377 | .RST(~resetn), 378 | .SHIFTIN1(1'b0), 379 | .SHIFTIN2(1'b0) 380 | ); 381 | 382 | ISERDESE2 #( 383 | .DATA_RATE("SDR"), 384 | .DATA_WIDTH(4), 385 | .DYN_CLKDIV_INV_EN("FALSE"), 386 | .DYN_CLK_INV_EN("FALSE"), 387 | .INIT_Q1(1'b0), 388 | .INIT_Q2(1'b0), 389 | .INIT_Q3(1'b0), 390 | .INIT_Q4(1'b0), 391 | .INTERFACE_TYPE("NETWORKING"), // MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE 392 | .IOBDELAY("NONE"), 393 | .NUM_CE(2), 394 | .OFB_USED("FALSE"), 395 | .SERDES_MODE("MASTER"), 396 | .SRVAL_Q1(1'b0), 397 | .SRVAL_Q2(1'b0), 398 | .SRVAL_Q3(1'b0), 399 | .SRVAL_Q4(1'b0) 400 | ) slave_iserdes ( 401 | .O(), 402 | .Q1(slave_iserdes_q[3]), 403 | .Q2(slave_iserdes_q[2]), 404 | .Q3(slave_iserdes_q[1]), 405 | .Q4(slave_iserdes_q[0]), 406 | .Q5(), 407 | .Q6(), 408 | .Q7(), 409 | .Q8(), 410 | .SHIFTOUT1(), 411 | .SHIFTOUT2(), 412 | .BITSLIP(1'b0), 413 | .CE1(1'b1), 414 | .CE2(1'b1), 415 | .CLKDIVP(1'b0), 416 | .CLK(clk4), 417 | .CLKB(~clk4), 418 | .CLKDIV(clk), 419 | .OCLK(1'b0), 420 | .DYNCLKDIVSEL(1'b0), 421 | .DYNCLKSEL(1'b0), 422 | .D(slave_d), 423 | .DDLY(1'b0), 424 | .OFB(1'b0), 425 | .OCLKB(1'b0), 426 | .RST(~resetn), 427 | .SHIFTIN1(1'b0), 428 | .SHIFTIN2(1'b0) 429 | ); 430 | endmodule 431 | -------------------------------------------------------------------------------- /demos/zybo/zybo.xdc: -------------------------------------------------------------------------------- 1 | 2 | set_property PACKAGE_PIN L16 [get_ports iclk] 3 | set_property IOSTANDARD LVCMOS33 [get_ports iclk] 4 | create_clock -add -name sys_clk_pin -period 8.00 -waveform {0 4} [get_ports iclk] 5 | 6 | set_property PACKAGE_PIN P14 [get_ports master_pin] 7 | set_property IOSTANDARD LVCMOS33 [get_ports master_pin] 8 | 9 | set_property PACKAGE_PIN V17 [get_ports slave_pin] 10 | set_property IOSTANDARD LVCMOS33 [get_ports slave_pin] 11 | 12 | set_property CFGBVS VCCO [current_design] 13 | set_property CONFIG_VOLTAGE 3.3 [current_design] 14 | 15 | set_property CLOCK_DEDICATED_ROUTE BACKBONE [get_nets iclk_IBUF] 16 | 17 | -------------------------------------------------------------------------------- /demos/zybo/zybo_prog.tcl: -------------------------------------------------------------------------------- 1 | 2 | # vivado -nojournal -nolog -mode batch -source zybo_prog.tcl 3 | 4 | connect_hw_server 5 | open_hw_target [lindex [get_hw_targets] 0] 6 | set_property PROGRAM.FILE zybo.bit [lindex [get_hw_devices] 1] 7 | program_hw_devices [lindex [get_hw_devices] 1] 8 | 9 | -------------------------------------------------------------------------------- /demos/zybo/zybo_synth.tcl: -------------------------------------------------------------------------------- 1 | 2 | # vivado -nojournal -nolog -mode batch -source zybo_synth.tcl 3 | 4 | create_project -part xc7z010clg400-2 -in_memory 5 | 6 | read_verilog zybo.v 7 | read_xdc zybo.xdc 8 | 9 | read_verilog ../../plinksrc/ponylink_master.v 10 | read_verilog ../../plinksrc/ponylink_slave.v 11 | read_verilog ../../plinksrc/ponylink_pack.v 12 | read_verilog ../../plinksrc/ponylink_txrx.v 13 | read_verilog ../../plinksrc/ponylink_8b10b.v 14 | read_verilog ../../plinksrc/ponylink_crc32.v 15 | 16 | synth_design -top top 17 | opt_design 18 | place_design 19 | route_design 20 | report_timing 21 | 22 | write_bitstream -force zybo.bit 23 | 24 | -------------------------------------------------------------------------------- /plinksrc/bitdist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import numpy as np 4 | 5 | encode_table = [ 6 | ( 0, 0, '0010111001', 0), 7 | ( 0, 1, '1101000110', 1), 8 | ( 1, 0, '0010101110', 0), 9 | ( 1, 1, '1101010001', 1), 10 | ( 2, 0, '0010101101', 0), 11 | ( 2, 1, '1101010010', 1), 12 | ( 3, 0, '1101100011', 1), 13 | ( 3, 1, '0010100011', 0), 14 | ( 4, 0, '0010101011', 0), 15 | ( 4, 1, '1101010100', 1), 16 | ( 5, 0, '1101100101', 1), 17 | ( 5, 1, '0010100101', 0), 18 | ( 6, 0, '1101100110', 1), 19 | ( 6, 1, '0010100110', 0), 20 | ( 7, 0, '1101000111', 1), 21 | ( 7, 1, '0010111000', 0), 22 | ( 8, 0, '0010100111', 0), 23 | ( 8, 1, '1101011000', 1), 24 | ( 9, 0, '1101101001', 1), 25 | ( 9, 1, '0010101001', 0), 26 | ( 10, 0, '1101101010', 1), 27 | ( 10, 1, '0010101010', 0), 28 | ( 11, 0, '1101001011', 1), 29 | ( 11, 1, '0010001011', 0), 30 | ( 12, 0, '1101101100', 1), 31 | ( 12, 1, '0010101100', 0), 32 | ( 13, 0, '1101001101', 1), 33 | ( 13, 1, '0010001101', 0), 34 | ( 14, 0, '1101001110', 1), 35 | ( 14, 1, '0010001110', 0), 36 | ( 15, 0, '0010111010', 0), 37 | ( 15, 1, '1101000101', 1), 38 | ( 16, 0, '0010110110', 0), 39 | ( 16, 1, '1101001001', 1), 40 | ( 17, 0, '1101110001', 1), 41 | ( 17, 1, '0010110001', 0), 42 | ( 18, 0, '1101110010', 1), 43 | ( 18, 1, '0010110010', 0), 44 | ( 19, 0, '1101010011', 1), 45 | ( 19, 1, '0010010011', 0), 46 | ( 20, 0, '1101110100', 1), 47 | ( 20, 1, '0010110100', 0), 48 | ( 21, 0, '1101010101', 1), 49 | ( 21, 1, '0010010101', 0), 50 | ( 22, 0, '1101010110', 1), 51 | ( 22, 1, '0010010110', 0), 52 | ( 23, 0, '0010010111', 0), 53 | ( 23, 1, '1101101000', 1), 54 | ( 24, 0, '0010110011', 0), 55 | ( 24, 1, '1101001100', 1), 56 | ( 25, 0, '1101011001', 1), 57 | ( 25, 1, '0010011001', 0), 58 | ( 26, 0, '1101011010', 1), 59 | ( 26, 1, '0010011010', 0), 60 | ( 27, 0, '0010011011', 0), 61 | ( 27, 1, '1101100100', 1), 62 | ( 28, 0, '1101011100', 1), 63 | ( 28, 1, '0010011100', 0), 64 | ( 29, 0, '0010011101', 0), 65 | ( 29, 1, '1101100010', 1), 66 | ( 30, 0, '0010011110', 0), 67 | ( 30, 1, '1101100001', 1), 68 | ( 31, 0, '0010110101', 0), 69 | ( 31, 1, '1101001010', 1), 70 | ( 32, 0, '1001111001', 1), 71 | ( 32, 1, '1001000110', 0), 72 | ( 33, 0, '1001101110', 1), 73 | ( 33, 1, '1001010001', 0), 74 | ( 34, 0, '1001101101', 1), 75 | ( 34, 1, '1001010010', 0), 76 | ( 35, 0, '1001100011', 0), 77 | ( 35, 1, '1001100011', 1), 78 | ( 36, 0, '1001101011', 1), 79 | ( 36, 1, '1001010100', 0), 80 | ( 37, 0, '1001100101', 0), 81 | ( 37, 1, '1001100101', 1), 82 | ( 38, 0, '1001100110', 0), 83 | ( 38, 1, '1001100110', 1), 84 | ( 39, 0, '1001000111', 0), 85 | ( 39, 1, '1001111000', 1), 86 | ( 40, 0, '1001100111', 1), 87 | ( 40, 1, '1001011000', 0), 88 | ( 41, 0, '1001101001', 0), 89 | ( 41, 1, '1001101001', 1), 90 | ( 42, 0, '1001101010', 0), 91 | ( 42, 1, '1001101010', 1), 92 | ( 43, 0, '1001001011', 0), 93 | ( 43, 1, '1001001011', 1), 94 | ( 44, 0, '1001101100', 0), 95 | ( 44, 1, '1001101100', 1), 96 | ( 45, 0, '1001001101', 0), 97 | ( 45, 1, '1001001101', 1), 98 | ( 46, 0, '1001001110', 0), 99 | ( 46, 1, '1001001110', 1), 100 | ( 47, 0, '1001111010', 1), 101 | ( 47, 1, '1001000101', 0), 102 | ( 48, 0, '1001110110', 1), 103 | ( 48, 1, '1001001001', 0), 104 | ( 49, 0, '1001110001', 0), 105 | ( 49, 1, '1001110001', 1), 106 | ( 50, 0, '1001110010', 0), 107 | ( 50, 1, '1001110010', 1), 108 | ( 51, 0, '1001010011', 0), 109 | ( 51, 1, '1001010011', 1), 110 | ( 52, 0, '1001110100', 0), 111 | ( 52, 1, '1001110100', 1), 112 | ( 53, 0, '1001010101', 0), 113 | ( 53, 1, '1001010101', 1), 114 | ( 54, 0, '1001010110', 0), 115 | ( 54, 1, '1001010110', 1), 116 | ( 55, 0, '1001010111', 1), 117 | ( 55, 1, '1001101000', 0), 118 | ( 56, 0, '1001110011', 1), 119 | ( 56, 1, '1001001100', 0), 120 | ( 57, 0, '1001011001', 0), 121 | ( 57, 1, '1001011001', 1), 122 | ( 58, 0, '1001011010', 0), 123 | ( 58, 1, '1001011010', 1), 124 | ( 59, 0, '1001011011', 1), 125 | ( 59, 1, '1001100100', 0), 126 | ( 60, 0, '1001011100', 0), 127 | ( 60, 1, '1001011100', 1), 128 | ( 61, 0, '1001011101', 1), 129 | ( 61, 1, '1001100010', 0), 130 | ( 62, 0, '1001011110', 1), 131 | ( 62, 1, '1001100001', 0), 132 | ( 63, 0, '1001110101', 1), 133 | ( 63, 1, '1001001010', 0), 134 | ( 64, 0, '1010111001', 1), 135 | ( 64, 1, '1010000110', 0), 136 | ( 65, 0, '1010101110', 1), 137 | ( 65, 1, '1010010001', 0), 138 | ( 66, 0, '1010101101', 1), 139 | ( 66, 1, '1010010010', 0), 140 | ( 67, 0, '1010100011', 0), 141 | ( 67, 1, '1010100011', 1), 142 | ( 68, 0, '1010101011', 1), 143 | ( 68, 1, '1010010100', 0), 144 | ( 69, 0, '1010100101', 0), 145 | ( 69, 1, '1010100101', 1), 146 | ( 70, 0, '1010100110', 0), 147 | ( 70, 1, '1010100110', 1), 148 | ( 71, 0, '1010000111', 0), 149 | ( 71, 1, '1010111000', 1), 150 | ( 72, 0, '1010100111', 1), 151 | ( 72, 1, '1010011000', 0), 152 | ( 73, 0, '1010101001', 0), 153 | ( 73, 1, '1010101001', 1), 154 | ( 74, 0, '1010101010', 0), 155 | ( 74, 1, '1010101010', 1), 156 | ( 75, 0, '1010001011', 0), 157 | ( 75, 1, '1010001011', 1), 158 | ( 76, 0, '1010101100', 0), 159 | ( 76, 1, '1010101100', 1), 160 | ( 77, 0, '1010001101', 0), 161 | ( 77, 1, '1010001101', 1), 162 | ( 78, 0, '1010001110', 0), 163 | ( 78, 1, '1010001110', 1), 164 | ( 79, 0, '1010111010', 1), 165 | ( 79, 1, '1010000101', 0), 166 | ( 80, 0, '1010110110', 1), 167 | ( 80, 1, '1010001001', 0), 168 | ( 81, 0, '1010110001', 0), 169 | ( 81, 1, '1010110001', 1), 170 | ( 82, 0, '1010110010', 0), 171 | ( 82, 1, '1010110010', 1), 172 | ( 83, 0, '1010010011', 0), 173 | ( 83, 1, '1010010011', 1), 174 | ( 84, 0, '1010110100', 0), 175 | ( 84, 1, '1010110100', 1), 176 | ( 85, 0, '1010010101', 0), 177 | ( 85, 1, '1010010101', 1), 178 | ( 86, 0, '1010010110', 0), 179 | ( 86, 1, '1010010110', 1), 180 | ( 87, 0, '1010010111', 1), 181 | ( 87, 1, '1010101000', 0), 182 | ( 88, 0, '1010110011', 1), 183 | ( 88, 1, '1010001100', 0), 184 | ( 89, 0, '1010011001', 0), 185 | ( 89, 1, '1010011001', 1), 186 | ( 90, 0, '1010011010', 0), 187 | ( 90, 1, '1010011010', 1), 188 | ( 91, 0, '1010011011', 1), 189 | ( 91, 1, '1010100100', 0), 190 | ( 92, 0, '1010011100', 0), 191 | ( 92, 1, '1010011100', 1), 192 | ( 93, 0, '1010011101', 1), 193 | ( 93, 1, '1010100010', 0), 194 | ( 94, 0, '1010011110', 1), 195 | ( 94, 1, '1010100001', 0), 196 | ( 95, 0, '1010110101', 1), 197 | ( 95, 1, '1010001010', 0), 198 | ( 96, 0, '1100111001', 1), 199 | ( 96, 1, '0011000110', 0), 200 | ( 97, 0, '1100101110', 1), 201 | ( 97, 1, '0011010001', 0), 202 | ( 98, 0, '1100101101', 1), 203 | ( 98, 1, '0011010010', 0), 204 | ( 99, 0, '0011100011', 0), 205 | ( 99, 1, '1100100011', 1), 206 | (100, 0, '1100101011', 1), 207 | (100, 1, '0011010100', 0), 208 | (101, 0, '0011100101', 0), 209 | (101, 1, '1100100101', 1), 210 | (102, 0, '0011100110', 0), 211 | (102, 1, '1100100110', 1), 212 | (103, 0, '0011000111', 0), 213 | (103, 1, '1100111000', 1), 214 | (104, 0, '1100100111', 1), 215 | (104, 1, '0011011000', 0), 216 | (105, 0, '0011101001', 0), 217 | (105, 1, '1100101001', 1), 218 | (106, 0, '0011101010', 0), 219 | (106, 1, '1100101010', 1), 220 | (107, 0, '0011001011', 0), 221 | (107, 1, '1100001011', 1), 222 | (108, 0, '0011101100', 0), 223 | (108, 1, '1100101100', 1), 224 | (109, 0, '0011001101', 0), 225 | (109, 1, '1100001101', 1), 226 | (110, 0, '0011001110', 0), 227 | (110, 1, '1100001110', 1), 228 | (111, 0, '1100111010', 1), 229 | (111, 1, '0011000101', 0), 230 | (112, 0, '1100110110', 1), 231 | (112, 1, '0011001001', 0), 232 | (113, 0, '0011110001', 0), 233 | (113, 1, '1100110001', 1), 234 | (114, 0, '0011110010', 0), 235 | (114, 1, '1100110010', 1), 236 | (115, 0, '0011010011', 0), 237 | (115, 1, '1100010011', 1), 238 | (116, 0, '0011110100', 0), 239 | (116, 1, '1100110100', 1), 240 | (117, 0, '0011010101', 0), 241 | (117, 1, '1100010101', 1), 242 | (118, 0, '0011010110', 0), 243 | (118, 1, '1100010110', 1), 244 | (119, 0, '1100010111', 1), 245 | (119, 1, '0011101000', 0), 246 | (120, 0, '1100110011', 1), 247 | (120, 1, '0011001100', 0), 248 | (121, 0, '0011011001', 0), 249 | (121, 1, '1100011001', 1), 250 | (122, 0, '0011011010', 0), 251 | (122, 1, '1100011010', 1), 252 | (123, 0, '1100011011', 1), 253 | (123, 1, '0011100100', 0), 254 | (124, 0, '0011011100', 0), 255 | (124, 1, '1100011100', 1), 256 | (125, 0, '1100011101', 1), 257 | (125, 1, '0011100010', 0), 258 | (126, 0, '1100011110', 1), 259 | (126, 1, '0011100001', 0), 260 | (127, 0, '1100110101', 1), 261 | (127, 1, '0011001010', 0), 262 | (128, 0, '0100111001', 0), 263 | (128, 1, '1011000110', 1), 264 | (129, 0, '0100101110', 0), 265 | (129, 1, '1011010001', 1), 266 | (130, 0, '0100101101', 0), 267 | (130, 1, '1011010010', 1), 268 | (131, 0, '1011100011', 1), 269 | (131, 1, '0100100011', 0), 270 | (132, 0, '0100101011', 0), 271 | (132, 1, '1011010100', 1), 272 | (133, 0, '1011100101', 1), 273 | (133, 1, '0100100101', 0), 274 | (134, 0, '1011100110', 1), 275 | (134, 1, '0100100110', 0), 276 | (135, 0, '1011000111', 1), 277 | (135, 1, '0100111000', 0), 278 | (136, 0, '0100100111', 0), 279 | (136, 1, '1011011000', 1), 280 | (137, 0, '1011101001', 1), 281 | (137, 1, '0100101001', 0), 282 | (138, 0, '1011101010', 1), 283 | (138, 1, '0100101010', 0), 284 | (139, 0, '1011001011', 1), 285 | (139, 1, '0100001011', 0), 286 | (140, 0, '1011101100', 1), 287 | (140, 1, '0100101100', 0), 288 | (141, 0, '1011001101', 1), 289 | (141, 1, '0100001101', 0), 290 | (142, 0, '1011001110', 1), 291 | (142, 1, '0100001110', 0), 292 | (143, 0, '0100111010', 0), 293 | (143, 1, '1011000101', 1), 294 | (144, 0, '0100110110', 0), 295 | (144, 1, '1011001001', 1), 296 | (145, 0, '1011110001', 1), 297 | (145, 1, '0100110001', 0), 298 | (146, 0, '1011110010', 1), 299 | (146, 1, '0100110010', 0), 300 | (147, 0, '1011010011', 1), 301 | (147, 1, '0100010011', 0), 302 | (148, 0, '1011110100', 1), 303 | (148, 1, '0100110100', 0), 304 | (149, 0, '1011010101', 1), 305 | (149, 1, '0100010101', 0), 306 | (150, 0, '1011010110', 1), 307 | (150, 1, '0100010110', 0), 308 | (151, 0, '0100010111', 0), 309 | (151, 1, '1011101000', 1), 310 | (152, 0, '0100110011', 0), 311 | (152, 1, '1011001100', 1), 312 | (153, 0, '1011011001', 1), 313 | (153, 1, '0100011001', 0), 314 | (154, 0, '1011011010', 1), 315 | (154, 1, '0100011010', 0), 316 | (155, 0, '0100011011', 0), 317 | (155, 1, '1011100100', 1), 318 | (156, 0, '1011011100', 1), 319 | (156, 1, '0100011100', 0), 320 | (157, 0, '0100011101', 0), 321 | (157, 1, '1011100010', 1), 322 | (158, 0, '0100011110', 0), 323 | (158, 1, '1011100001', 1), 324 | (159, 0, '0100110101', 0), 325 | (159, 1, '1011001010', 1), 326 | (160, 0, '0101111001', 1), 327 | (160, 1, '0101000110', 0), 328 | (161, 0, '0101101110', 1), 329 | (161, 1, '0101010001', 0), 330 | (162, 0, '0101101101', 1), 331 | (162, 1, '0101010010', 0), 332 | (163, 0, '0101100011', 0), 333 | (163, 1, '0101100011', 1), 334 | (164, 0, '0101101011', 1), 335 | (164, 1, '0101010100', 0), 336 | (165, 0, '0101100101', 0), 337 | (165, 1, '0101100101', 1), 338 | (166, 0, '0101100110', 0), 339 | (166, 1, '0101100110', 1), 340 | (167, 0, '0101000111', 0), 341 | (167, 1, '0101111000', 1), 342 | (168, 0, '0101100111', 1), 343 | (168, 1, '0101011000', 0), 344 | (169, 0, '0101101001', 0), 345 | (169, 1, '0101101001', 1), 346 | (170, 0, '0101101010', 0), 347 | (170, 1, '0101101010', 1), 348 | (171, 0, '0101001011', 0), 349 | (171, 1, '0101001011', 1), 350 | (172, 0, '0101101100', 0), 351 | (172, 1, '0101101100', 1), 352 | (173, 0, '0101001101', 0), 353 | (173, 1, '0101001101', 1), 354 | (174, 0, '0101001110', 0), 355 | (174, 1, '0101001110', 1), 356 | (175, 0, '0101111010', 1), 357 | (175, 1, '0101000101', 0), 358 | (176, 0, '0101110110', 1), 359 | (176, 1, '0101001001', 0), 360 | (177, 0, '0101110001', 0), 361 | (177, 1, '0101110001', 1), 362 | (178, 0, '0101110010', 0), 363 | (178, 1, '0101110010', 1), 364 | (179, 0, '0101010011', 0), 365 | (179, 1, '0101010011', 1), 366 | (180, 0, '0101110100', 0), 367 | (180, 1, '0101110100', 1), 368 | (181, 0, '0101010101', 0), 369 | (181, 1, '0101010101', 1), 370 | (182, 0, '0101010110', 0), 371 | (182, 1, '0101010110', 1), 372 | (183, 0, '0101010111', 1), 373 | (183, 1, '0101101000', 0), 374 | (184, 0, '0101110011', 1), 375 | (184, 1, '0101001100', 0), 376 | (185, 0, '0101011001', 0), 377 | (185, 1, '0101011001', 1), 378 | (186, 0, '0101011010', 0), 379 | (186, 1, '0101011010', 1), 380 | (187, 0, '0101011011', 1), 381 | (187, 1, '0101100100', 0), 382 | (188, 0, '0101011100', 0), 383 | (188, 1, '0101011100', 1), 384 | (189, 0, '0101011101', 1), 385 | (189, 1, '0101100010', 0), 386 | (190, 0, '0101011110', 1), 387 | (190, 1, '0101100001', 0), 388 | (191, 0, '0101110101', 1), 389 | (191, 1, '0101001010', 0), 390 | (192, 0, '0110111001', 1), 391 | (192, 1, '0110000110', 0), 392 | (193, 0, '0110101110', 1), 393 | (193, 1, '0110010001', 0), 394 | (194, 0, '0110101101', 1), 395 | (194, 1, '0110010010', 0), 396 | (195, 0, '0110100011', 0), 397 | (195, 1, '0110100011', 1), 398 | (196, 0, '0110101011', 1), 399 | (196, 1, '0110010100', 0), 400 | (197, 0, '0110100101', 0), 401 | (197, 1, '0110100101', 1), 402 | (198, 0, '0110100110', 0), 403 | (198, 1, '0110100110', 1), 404 | (199, 0, '0110000111', 0), 405 | (199, 1, '0110111000', 1), 406 | (200, 0, '0110100111', 1), 407 | (200, 1, '0110011000', 0), 408 | (201, 0, '0110101001', 0), 409 | (201, 1, '0110101001', 1), 410 | (202, 0, '0110101010', 0), 411 | (202, 1, '0110101010', 1), 412 | (203, 0, '0110001011', 0), 413 | (203, 1, '0110001011', 1), 414 | (204, 0, '0110101100', 0), 415 | (204, 1, '0110101100', 1), 416 | (205, 0, '0110001101', 0), 417 | (205, 1, '0110001101', 1), 418 | (206, 0, '0110001110', 0), 419 | (206, 1, '0110001110', 1), 420 | (207, 0, '0110111010', 1), 421 | (207, 1, '0110000101', 0), 422 | (208, 0, '0110110110', 1), 423 | (208, 1, '0110001001', 0), 424 | (209, 0, '0110110001', 0), 425 | (209, 1, '0110110001', 1), 426 | (210, 0, '0110110010', 0), 427 | (210, 1, '0110110010', 1), 428 | (211, 0, '0110010011', 0), 429 | (211, 1, '0110010011', 1), 430 | (212, 0, '0110110100', 0), 431 | (212, 1, '0110110100', 1), 432 | (213, 0, '0110010101', 0), 433 | (213, 1, '0110010101', 1), 434 | (214, 0, '0110010110', 0), 435 | (214, 1, '0110010110', 1), 436 | (215, 0, '0110010111', 1), 437 | (215, 1, '0110101000', 0), 438 | (216, 0, '0110110011', 1), 439 | (216, 1, '0110001100', 0), 440 | (217, 0, '0110011001', 0), 441 | (217, 1, '0110011001', 1), 442 | (218, 0, '0110011010', 0), 443 | (218, 1, '0110011010', 1), 444 | (219, 0, '0110011011', 1), 445 | (219, 1, '0110100100', 0), 446 | (220, 0, '0110011100', 0), 447 | (220, 1, '0110011100', 1), 448 | (221, 0, '0110011101', 1), 449 | (221, 1, '0110100010', 0), 450 | (222, 0, '0110011110', 1), 451 | (222, 1, '0110100001', 0), 452 | (223, 0, '0110110101', 1), 453 | (223, 1, '0110001010', 0), 454 | (224, 0, '1000111001', 0), 455 | (224, 1, '0111000110', 1), 456 | (225, 0, '1000101110', 0), 457 | (225, 1, '0111010001', 1), 458 | (226, 0, '1000101101', 0), 459 | (226, 1, '0111010010', 1), 460 | (227, 0, '0111100011', 1), 461 | (227, 1, '1000100011', 0), 462 | (228, 0, '1000101011', 0), 463 | (228, 1, '0111010100', 1), 464 | (229, 0, '0111100101', 1), 465 | (229, 1, '1000100101', 0), 466 | (230, 0, '0111100110', 1), 467 | (230, 1, '1000100110', 0), 468 | (231, 0, '0111000111', 1), 469 | (231, 1, '1000111000', 0), 470 | (232, 0, '1000100111', 0), 471 | (232, 1, '0111011000', 1), 472 | (233, 0, '0111101001', 1), 473 | (233, 1, '1000101001', 0), 474 | (234, 0, '0111101010', 1), 475 | (234, 1, '1000101010', 0), 476 | (235, 0, '0111001011', 1), 477 | (235, 1, '0001001011', 0), 478 | (236, 0, '0111101100', 1), 479 | (236, 1, '1000101100', 0), 480 | (237, 0, '0111001101', 1), 481 | (237, 1, '0001001101', 0), 482 | (238, 0, '0111001110', 1), 483 | (238, 1, '0001001110', 0), 484 | (239, 0, '1000111010', 0), 485 | (239, 1, '0111000101', 1), 486 | (240, 0, '1000110110', 0), 487 | (240, 1, '0111001001', 1), 488 | (241, 0, '1110110001', 1), 489 | (241, 1, '1000110001', 0), 490 | (242, 0, '1110110010', 1), 491 | (242, 1, '1000110010', 0), 492 | (243, 0, '0111010011', 1), 493 | (243, 1, '1000010011', 0), 494 | (244, 0, '1110110100', 1), 495 | (244, 1, '1000110100', 0), 496 | (245, 0, '0111010101', 1), 497 | (245, 1, '1000010101', 0), 498 | (246, 0, '0111010110', 1), 499 | (246, 1, '1000010110', 0), 500 | (247, 0, '1000010111', 0), 501 | (247, 1, '0111101000', 1), 502 | (248, 0, '1000110011', 0), 503 | (248, 1, '0111001100', 1), 504 | (249, 0, '0111011001', 1), 505 | (249, 1, '1000011001', 0), 506 | (250, 0, '0111011010', 1), 507 | (250, 1, '1000011010', 0), 508 | (251, 0, '1000011011', 0), 509 | (251, 1, '0111100100', 1), 510 | (252, 0, '0111011100', 1), 511 | (252, 1, '1000011100', 0), 512 | (253, 0, '1000011101', 0), 513 | (253, 1, '0111100010', 1), 514 | (254, 0, '1000011110', 0), 515 | (254, 1, '0111100001', 1), 516 | (255, 0, '1000110101', 0), 517 | (255, 1, '0111001010', 1), 518 | (284, 0, '0010111100', 0), 519 | (284, 1, '1101000011', 1), 520 | (316, 0, '1001111100', 1), 521 | (316, 1, '0110000011', 0), 522 | (348, 0, '1010111100', 1), 523 | (348, 1, '0101000011', 0), 524 | (380, 0, '1100111100', 1), 525 | (380, 1, '0011000011', 0), 526 | (412, 0, '0100111100', 0), 527 | (412, 1, '1011000011', 1), 528 | (444, 0, '0101111100', 1), 529 | (444, 1, '1010000011', 0), 530 | (476, 0, '0110111100', 1), 531 | (476, 1, '1001000011', 0), 532 | (503, 0, '0001010111', 0), 533 | (503, 1, '1110101000', 1), 534 | (507, 0, '0001011011', 0), 535 | (507, 1, '1110100100', 1), 536 | (508, 0, '0001111100', 0), 537 | (508, 1, '1110000011', 1), 538 | (509, 0, '0001011101', 0), 539 | (509, 1, '1110100010', 1), 540 | (510, 0, '0001011110', 0), 541 | (510, 1, '1110100001', 1), 542 | ] 543 | 544 | encode_dict = dict() 545 | 546 | for dat in encode_table: 547 | if not (dat[0] in encode_dict): 548 | encode_dict[dat[0]] = [ "", -1, "", -1 ] 549 | if dat[1] == 0: 550 | encode_dict[dat[0]][0] = dat[2] 551 | encode_dict[dat[0]][1] = dat[3] 552 | else: 553 | encode_dict[dat[0]][2] = dat[2] 554 | encode_dict[dat[0]][3] = dat[3] 555 | 556 | disparity=0 557 | sequence="" 558 | 559 | for i in range(1000000): 560 | if i % 69 == 0: 561 | idx = 0x1fc 562 | disparity = 0 563 | sequence += "x" 564 | elif i % 69 == 67: 565 | idx = 0x1bc 566 | else: 567 | idx = np.random.randint(256) 568 | assert idx in encode_dict 569 | assert encode_dict[idx][2*disparity+1] >= 0 570 | sequence += encode_dict[idx][2*disparity][::-1] 571 | disparity = encode_dict[idx][2*disparity+1] 572 | 573 | frequency = np.zeros(5) 574 | state = 'x' 575 | count = 1 576 | 577 | for i in range(len(sequence)): 578 | if state != sequence[i]: 579 | if state != 'x': 580 | frequency[count-1] += count 581 | state = sequence[i] 582 | count = 1 583 | else: 584 | count += 1 585 | 586 | print(frequency / np.sum(frequency)) 587 | -------------------------------------------------------------------------------- /plinksrc/ponylink_8b10b.v: -------------------------------------------------------------------------------- 1 | // Chuck Benz, Hollis, NH Copyright (c)2002 2 | // 3 | // The information and description contained herein is the 4 | // property of Chuck Benz. 5 | // 6 | // Permission is granted for any reuse of this information 7 | // and description as long as this copyright notice is 8 | // preserved. Modifications may be made as long as this 9 | // notice is preserved. 10 | 11 | // per Widmer and Franaszek 12 | 13 | // Minor modifications by Clifford Wolf for ponylink 14 | 15 | `timescale 1 ns / 1 ps 16 | 17 | module ponylink_decode_8b10b (datain, dispin, dataout, dispout, code_err, disp_err) ; 18 | input [9:0] datain ; 19 | input dispin ; 20 | output [8:0] dataout ; 21 | output dispout ; 22 | output code_err ; 23 | output disp_err ; 24 | 25 | wire ai = datain[0] ; 26 | wire bi = datain[1] ; 27 | wire ci = datain[2] ; 28 | wire di = datain[3] ; 29 | wire ei = datain[4] ; 30 | wire ii = datain[5] ; 31 | wire fi = datain[6] ; 32 | wire gi = datain[7] ; 33 | wire hi = datain[8] ; 34 | wire ji = datain[9] ; 35 | 36 | wire aeqb = (ai & bi) | (!ai & !bi) ; 37 | wire ceqd = (ci & di) | (!ci & !di) ; 38 | wire p22 = (ai & bi & !ci & !di) | 39 | (ci & di & !ai & !bi) | 40 | ( !aeqb & !ceqd) ; 41 | wire p13 = ( !aeqb & !ci & !di) | 42 | ( !ceqd & !ai & !bi) ; 43 | wire p31 = ( !aeqb & ci & di) | 44 | ( !ceqd & ai & bi) ; 45 | 46 | wire p40 = ai & bi & ci & di ; 47 | wire p04 = !ai & !bi & !ci & !di ; 48 | 49 | wire disp6a = p31 | (p22 & dispin) ; // pos disp if p22 and was pos, or p31. 50 | wire disp6a2 = p31 & dispin ; // disp is ++ after 4 bits 51 | wire disp6a0 = p13 & ! dispin ; // -- disp after 4 bits 52 | 53 | wire disp6b = (((ei & ii & ! disp6a0) | (disp6a & (ei | ii)) | disp6a2 | 54 | (ei & ii & di)) & (ei | ii | di)) ; 55 | 56 | // The 5B/6B decoding special cases where ABCDE != abcde 57 | 58 | wire p22bceeqi = p22 & bi & ci & (ei == ii) ; 59 | wire p22bncneeqi = p22 & !bi & !ci & (ei == ii) ; 60 | wire p13in = p13 & !ii ; 61 | wire p31i = p31 & ii ; 62 | wire p13dei = p13 & di & ei & ii ; 63 | wire p22aceeqi = p22 & ai & ci & (ei == ii) ; 64 | wire p22ancneeqi = p22 & !ai & !ci & (ei == ii) ; 65 | wire p13en = p13 & !ei ; 66 | wire anbnenin = !ai & !bi & !ei & !ii ; 67 | wire abei = ai & bi & ei & ii ; 68 | wire cdei = ci & di & ei & ii ; 69 | wire cndnenin = !ci & !di & !ei & !ii ; 70 | 71 | // non-zero disparity cases: 72 | wire p22enin = p22 & !ei & !ii ; 73 | wire p22ei = p22 & ei & ii ; 74 | //wire p13in = p12 & !ii ; 75 | //wire p31i = p31 & ii ; 76 | wire p31dnenin = p31 & !di & !ei & !ii ; 77 | //wire p13dei = p13 & di & ei & ii ; 78 | wire p31e = p31 & ei ; 79 | 80 | wire compa = p22bncneeqi | p31i | p13dei | p22ancneeqi | 81 | p13en | abei | cndnenin ; 82 | wire compb = p22bceeqi | p31i | p13dei | p22aceeqi | 83 | p13en | abei | cndnenin ; 84 | wire compc = p22bceeqi | p31i | p13dei | p22ancneeqi | 85 | p13en | anbnenin | cndnenin ; 86 | wire compd = p22bncneeqi | p31i | p13dei | p22aceeqi | 87 | p13en | abei | cndnenin ; 88 | wire compe = p22bncneeqi | p13in | p13dei | p22ancneeqi | 89 | p13en | anbnenin | cndnenin ; 90 | 91 | wire ao = ai ^ compa ; 92 | wire bo = bi ^ compb ; 93 | wire co = ci ^ compc ; 94 | wire do = di ^ compd ; 95 | wire eo = ei ^ compe ; 96 | 97 | wire feqg = (fi & gi) | (!fi & !gi) ; 98 | wire heqj = (hi & ji) | (!hi & !ji) ; 99 | wire fghj22 = (fi & gi & !hi & !ji) | 100 | (!fi & !gi & hi & ji) | 101 | ( !feqg & !heqj) ; 102 | wire fghjp13 = ( !feqg & !hi & !ji) | 103 | ( !heqj & !fi & !gi) ; 104 | wire fghjp31 = ( (!feqg) & hi & ji) | 105 | ( !heqj & fi & gi) ; 106 | 107 | wire dispout = (fghjp31 | (disp6b & fghj22) | (hi & ji)) & (hi | ji) ; 108 | 109 | wire ko = ( (ci & di & ei & ii) | ( !ci & !di & !ei & !ii) | 110 | (p13 & !ei & ii & gi & hi & ji) | 111 | (p31 & ei & !ii & !gi & !hi & !ji)) ; 112 | 113 | wire alt7 = (fi & !gi & !hi & // 1000 cases, where disp6b is 1 114 | ((dispin & ci & di & !ei & !ii) | ko | 115 | (dispin & !ci & di & !ei & !ii))) | 116 | (!fi & gi & hi & // 0111 cases, where disp6b is 0 117 | (( !dispin & !ci & !di & ei & ii) | ko | 118 | ( !dispin & ci & !di & ei & ii))) ; 119 | 120 | wire k28 = (ci & di & ei & ii) | ! (ci | di | ei | ii) ; 121 | // k28 with positive disp into fghi - .1, .2, .5, and .6 special cases 122 | wire k28p = ! (ci | di | ei | ii) ; 123 | wire fo = (ji & !fi & (hi | !gi | k28p)) | 124 | (fi & !ji & (!hi | gi | !k28p)) | 125 | (k28p & gi & hi) | 126 | (!k28p & !gi & !hi) ; 127 | wire go = (ji & !fi & (hi | !gi | !k28p)) | 128 | (fi & !ji & (!hi | gi |k28p)) | 129 | (!k28p & gi & hi) | 130 | (k28p & !gi & !hi) ; 131 | wire ho = ((ji ^ hi) & ! ((!fi & gi & !hi & ji & !k28p) | (!fi & gi & hi & !ji & k28p) | 132 | (fi & !gi & !hi & ji & !k28p) | (fi & !gi & hi & !ji & k28p))) | 133 | (!fi & gi & hi & ji) | (fi & !gi & !hi & !ji) ; 134 | 135 | wire disp6p = (p31 & (ei | ii)) | (p22 & ei & ii) ; 136 | wire disp6n = (p13 & ! (ei & ii)) | (p22 & !ei & !ii) ; 137 | wire disp4p = fghjp31 ; 138 | wire disp4n = fghjp13 ; 139 | 140 | assign code_err = p40 | p04 | (fi & gi & hi & ji) | (!fi & !gi & !hi & !ji) | 141 | (p13 & !ei & !ii) | (p31 & ei & ii) | 142 | (ei & ii & fi & gi & hi) | (!ei & !ii & !fi & !gi & !hi) | 143 | (ei & !ii & gi & hi & ji) | (!ei & ii & !gi & !hi & !ji) | 144 | (!p31 & ei & !ii & !gi & !hi & !ji) | 145 | (!p13 & !ei & ii & gi & hi & ji) | 146 | (((ei & ii & !gi & !hi & !ji) | 147 | (!ei & !ii & gi & hi & ji)) & 148 | ! ((ci & di & ei) | (!ci & !di & !ei))) | 149 | (disp6p & disp4p) | (disp6n & disp4n) | 150 | (ai & bi & ci & !ei & !ii & ((!fi & !gi) | fghjp13)) | 151 | (!ai & !bi & !ci & ei & ii & ((fi & gi) | fghjp31)) | 152 | (fi & gi & !hi & !ji & disp6p) | 153 | (!fi & !gi & hi & ji & disp6n) | 154 | (ci & di & ei & ii & !fi & !gi & !hi) | 155 | (!ci & !di & !ei & !ii & fi & gi & hi) ; 156 | 157 | assign dataout = {ko, ho, go, fo, eo, do, co, bo, ao} ; 158 | 159 | // my disp err fires for any legal codes that violate disparity, may fire for illegal codes 160 | assign disp_err = ((dispin & disp6p) | (disp6n & !dispin) | 161 | (dispin & !disp6n & fi & gi) | 162 | (dispin & ai & bi & ci) | 163 | (dispin & !disp6n & disp4p) | 164 | (!dispin & !disp6p & !fi & !gi) | 165 | (!dispin & !ai & !bi & !ci) | 166 | (!dispin & !disp6p & disp4n) | 167 | (disp6p & disp4p) | (disp6n & disp4n)) ; 168 | endmodule 169 | 170 | // ======================================================================================== 171 | 172 | module ponylink_encode_8b10b (datain, dispin, dataout, dispout) ; 173 | input [8:0] datain ; 174 | input dispin ; // 0 = neg disp; 1 = pos disp 175 | output [9:0] dataout ; 176 | output dispout ; 177 | 178 | 179 | wire ai = datain[0] ; 180 | wire bi = datain[1] ; 181 | wire ci = datain[2] ; 182 | wire di = datain[3] ; 183 | wire ei = datain[4] ; 184 | wire fi = datain[5] ; 185 | wire gi = datain[6] ; 186 | wire hi = datain[7] ; 187 | wire ki = datain[8] ; 188 | 189 | wire aeqb = (ai & bi) | (!ai & !bi) ; 190 | wire ceqd = (ci & di) | (!ci & !di) ; 191 | wire l22 = (ai & bi & !ci & !di) | 192 | (ci & di & !ai & !bi) | 193 | ( !aeqb & !ceqd) ; 194 | wire l40 = ai & bi & ci & di ; 195 | wire l04 = !ai & !bi & !ci & !di ; 196 | wire l13 = ( !aeqb & !ci & !di) | 197 | ( !ceqd & !ai & !bi) ; 198 | wire l31 = ( !aeqb & ci & di) | 199 | ( !ceqd & ai & bi) ; 200 | 201 | // The 5B/6B encoding 202 | 203 | wire ao = ai ; 204 | wire bo = (bi & !l40) | l04 ; 205 | wire co = l04 | ci | (ei & di & !ci & !bi & !ai) ; 206 | wire do = di & ! (ai & bi & ci) ; 207 | wire eo = (ei | l13) & ! (ei & di & !ci & !bi & !ai) ; 208 | wire io = (l22 & !ei) | 209 | (ei & !di & !ci & !(ai&bi)) | // D16, D17, D18 210 | (ei & l40) | 211 | (ki & ei & di & ci & !bi & !ai) | // K.28 212 | (ei & !di & ci & !bi & !ai) ; 213 | 214 | // pds16 indicates cases where d-1 is assumed + to get our encoded value 215 | wire pd1s6 = (ei & di & !ci & !bi & !ai) | (!ei & !l22 & !l31) ; 216 | // nds16 indicates cases where d-1 is assumed - to get our encoded value 217 | wire nd1s6 = ki | (ei & !l22 & !l13) | (!ei & !di & ci & bi & ai) ; 218 | 219 | // ndos6 is pds16 cases where d-1 is + yields - disp out - all of them 220 | wire ndos6 = pd1s6 ; 221 | // pdos6 is nds16 cases where d-1 is - yields + disp out - all but one 222 | wire pdos6 = ki | (ei & !l22 & !l13) ; 223 | 224 | 225 | // some Dx.7 and all Kx.7 cases result in run length of 5 case unless 226 | // an alternate coding is used (referred to as Dx.A7, normal is Dx.P7) 227 | // specifically, D11, D13, D14, D17, D18, D19. 228 | wire alt7 = fi & gi & hi & (ki | 229 | (dispin ? (!ei & di & l31) : (ei & !di & l13))) ; 230 | 231 | 232 | wire fo = fi & ! alt7 ; 233 | wire go = gi | (!fi & !gi & !hi) ; 234 | wire ho = hi ; 235 | wire jo = (!hi & (gi ^ fi)) | alt7 ; 236 | 237 | // nd1s4 is cases where d-1 is assumed - to get our encoded value 238 | wire nd1s4 = fi & gi ; 239 | // pd1s4 is cases where d-1 is assumed + to get our encoded value 240 | wire pd1s4 = (!fi & !gi) | (ki & ((fi & !gi) | (!fi & gi))) ; 241 | 242 | // ndos4 is pd1s4 cases where d-1 is + yields - disp out - just some 243 | wire ndos4 = (!fi & !gi) ; 244 | // pdos4 is nd1s4 cases where d-1 is - yields + disp out 245 | wire pdos4 = fi & gi & hi ; 246 | 247 | // only legal K codes are K28.0->.7, K23/27/29/30.7 248 | // K28.0->7 is ei=di=ci=1,bi=ai=0 249 | // K23 is 10111 250 | // K27 is 11011 251 | // K29 is 11101 252 | // K30 is 11110 - so K23/27/29/30 are ei & l31 253 | wire illegalk = ki & 254 | (ai | bi | !ci | !di | !ei) & // not K28.0->7 255 | (!fi | !gi | !hi | !ei | !l31) ; // not K23/27/29/30.7 256 | 257 | // now determine whether to do the complementing 258 | // complement if prev disp is - and pd1s6 is set, or + and nd1s6 is set 259 | wire compls6 = (pd1s6 & !dispin) | (nd1s6 & dispin) ; 260 | 261 | // disparity out of 5b6b is disp in with pdso6 and ndso6 262 | // pds16 indicates cases where d-1 is assumed + to get our encoded value 263 | // ndos6 is cases where d-1 is + yields - disp out 264 | // nds16 indicates cases where d-1 is assumed - to get our encoded value 265 | // pdos6 is cases where d-1 is - yields + disp out 266 | // disp toggles in all ndis16 cases, and all but that 1 nds16 case 267 | 268 | wire disp6 = dispin ^ (ndos6 | pdos6) ; 269 | 270 | wire compls4 = (pd1s4 & !disp6) | (nd1s4 & disp6) ; 271 | assign dispout = disp6 ^ (ndos4 | pdos4) ; 272 | 273 | assign dataout = {(jo ^ compls4), (ho ^ compls4), 274 | (go ^ compls4), (fo ^ compls4), 275 | (io ^ compls6), (eo ^ compls6), 276 | (do ^ compls6), (co ^ compls6), 277 | (bo ^ compls6), (ao ^ compls6)} ; 278 | endmodule 279 | 280 | // ----------------------------------------------------------------------- 281 | // Wrappers with extra functionality for PonyLink. Added by Clifford Wolf. 282 | 283 | module ponylink_encode_8b10b_xtra (clk, datain, dispin, dataout, dispout); 284 | input clk; 285 | input [8:0] datain; 286 | input dispin; 287 | output [9:0] dataout; 288 | output dispout; 289 | wire [9:0] dataout_t; 290 | reg lastbit; 291 | 292 | ponylink_encode_8b10b encoder (datain, dispin, dataout_t, dispout); 293 | 294 | assign dataout = datain == 9'h100 ? {9'b0, ~lastbit} : dataout_t; 295 | always @(posedge clk) lastbit <= dataout[9]; 296 | endmodule 297 | 298 | module ponylink_decode_8b10b_xtra (clk, reset, enable, rstdetect, datain, dataout, recv_error); 299 | parameter RECVRESET = 0; 300 | 301 | input clk, reset, enable; 302 | output reg rstdetect; 303 | input [9:0] datain; 304 | output [8:0] dataout; 305 | output recv_error; 306 | 307 | reg dispout_q; 308 | wire dispin, dispout; 309 | 310 | always @(posedge clk) 311 | if (enable) dispout_q <= dispout; 312 | 313 | assign dispin = dispout_q && (datain != 10'b0001111100); 314 | 315 | wire code_err, disp_err; 316 | assign recv_error = (code_err | disp_err) && dataout != 9'h1fc; 317 | 318 | ponylink_decode_8b10b decoder (datain, dispin, dataout, dispout, code_err, disp_err); 319 | 320 | reg [3:0] reset_state; 321 | 322 | always @(posedge clk) begin 323 | rstdetect <= 0; 324 | if (reset) begin 325 | reset_state <= 0; 326 | end else 327 | if (enable) begin 328 | reset_state <= 0; 329 | if (!recv_error) begin 330 | if (dataout == 9'h1fc) 331 | reset_state <= 2; 332 | else 333 | if (2 <= reset_state && reset_state < 6) begin 334 | if (dataout == (RECVRESET ? 9'h1fd : 9'h1fe)) 335 | reset_state <= reset_state + 1'b1; 336 | end else 337 | if (6 <= reset_state) begin 338 | if (dataout == reset_state) 339 | reset_state <= reset_state + 1'b1; 340 | end 341 | end 342 | end 343 | if (reset_state == 15) begin 344 | rstdetect <= 1; 345 | reset_state <= 0; 346 | end 347 | end 348 | endmodule 349 | 350 | -------------------------------------------------------------------------------- /plinksrc/ponylink_crc32.v: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Copyright (C) 2009 OutputLogic.com 3 | // This source file may be used and distributed without restriction 4 | // provided that this copyright statement is not removed from the file 5 | // and that any derivative work contains the original copyright notice 6 | // and the associated disclaimer. 7 | // 8 | // THIS SOURCE FILE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS 9 | // OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 10 | // WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 11 | //----------------------------------------------------------------------------- 12 | // CRC module for data[7:0] , crc[31:0]=1+x^1+x^2+x^4+x^5+x^7+x^8+x^10+x^11+x^12+x^16+x^22+x^23+x^26+x^32; 13 | //----------------------------------------------------------------------------- 14 | module ponylink_crc32( 15 | input [7:0] data_in, 16 | input crc_en, 17 | output [31:0] crc_out, 18 | input rst, 19 | input clk); 20 | 21 | reg [31:0] lfsr_q,lfsr_c; 22 | 23 | assign crc_out = lfsr_q; 24 | 25 | always @(*) begin 26 | lfsr_c[0] = lfsr_q[24] ^ lfsr_q[30] ^ data_in[0] ^ data_in[6]; 27 | lfsr_c[1] = lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[0] ^ data_in[1] ^ data_in[6] ^ data_in[7]; 28 | lfsr_c[2] = lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[0] ^ data_in[1] ^ data_in[2] ^ data_in[6] ^ data_in[7]; 29 | lfsr_c[3] = lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[31] ^ data_in[1] ^ data_in[2] ^ data_in[3] ^ data_in[7]; 30 | lfsr_c[4] = lfsr_q[24] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[28] ^ lfsr_q[30] ^ data_in[0] ^ data_in[2] ^ data_in[3] ^ data_in[4] ^ data_in[6]; 31 | lfsr_c[5] = lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[27] ^ lfsr_q[28] ^ lfsr_q[29] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[0] ^ data_in[1] ^ data_in[3] ^ data_in[4] ^ data_in[5] ^ data_in[6] ^ data_in[7]; 32 | lfsr_c[6] = lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[28] ^ lfsr_q[29] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[1] ^ data_in[2] ^ data_in[4] ^ data_in[5] ^ data_in[6] ^ data_in[7]; 33 | lfsr_c[7] = lfsr_q[24] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[29] ^ lfsr_q[31] ^ data_in[0] ^ data_in[2] ^ data_in[3] ^ data_in[5] ^ data_in[7]; 34 | lfsr_c[8] = lfsr_q[0] ^ lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[27] ^ lfsr_q[28] ^ data_in[0] ^ data_in[1] ^ data_in[3] ^ data_in[4]; 35 | lfsr_c[9] = lfsr_q[1] ^ lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[28] ^ lfsr_q[29] ^ data_in[1] ^ data_in[2] ^ data_in[4] ^ data_in[5]; 36 | lfsr_c[10] = lfsr_q[2] ^ lfsr_q[24] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[29] ^ data_in[0] ^ data_in[2] ^ data_in[3] ^ data_in[5]; 37 | lfsr_c[11] = lfsr_q[3] ^ lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[27] ^ lfsr_q[28] ^ data_in[0] ^ data_in[1] ^ data_in[3] ^ data_in[4]; 38 | lfsr_c[12] = lfsr_q[4] ^ lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[28] ^ lfsr_q[29] ^ lfsr_q[30] ^ data_in[0] ^ data_in[1] ^ data_in[2] ^ data_in[4] ^ data_in[5] ^ data_in[6]; 39 | lfsr_c[13] = lfsr_q[5] ^ lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[29] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[1] ^ data_in[2] ^ data_in[3] ^ data_in[5] ^ data_in[6] ^ data_in[7]; 40 | lfsr_c[14] = lfsr_q[6] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[28] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[2] ^ data_in[3] ^ data_in[4] ^ data_in[6] ^ data_in[7]; 41 | lfsr_c[15] = lfsr_q[7] ^ lfsr_q[27] ^ lfsr_q[28] ^ lfsr_q[29] ^ lfsr_q[31] ^ data_in[3] ^ data_in[4] ^ data_in[5] ^ data_in[7]; 42 | lfsr_c[16] = lfsr_q[8] ^ lfsr_q[24] ^ lfsr_q[28] ^ lfsr_q[29] ^ data_in[0] ^ data_in[4] ^ data_in[5]; 43 | lfsr_c[17] = lfsr_q[9] ^ lfsr_q[25] ^ lfsr_q[29] ^ lfsr_q[30] ^ data_in[1] ^ data_in[5] ^ data_in[6]; 44 | lfsr_c[18] = lfsr_q[10] ^ lfsr_q[26] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[2] ^ data_in[6] ^ data_in[7]; 45 | lfsr_c[19] = lfsr_q[11] ^ lfsr_q[27] ^ lfsr_q[31] ^ data_in[3] ^ data_in[7]; 46 | lfsr_c[20] = lfsr_q[12] ^ lfsr_q[28] ^ data_in[4]; 47 | lfsr_c[21] = lfsr_q[13] ^ lfsr_q[29] ^ data_in[5]; 48 | lfsr_c[22] = lfsr_q[14] ^ lfsr_q[24] ^ data_in[0]; 49 | lfsr_c[23] = lfsr_q[15] ^ lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[30] ^ data_in[0] ^ data_in[1] ^ data_in[6]; 50 | lfsr_c[24] = lfsr_q[16] ^ lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[31] ^ data_in[1] ^ data_in[2] ^ data_in[7]; 51 | lfsr_c[25] = lfsr_q[17] ^ lfsr_q[26] ^ lfsr_q[27] ^ data_in[2] ^ data_in[3]; 52 | lfsr_c[26] = lfsr_q[18] ^ lfsr_q[24] ^ lfsr_q[27] ^ lfsr_q[28] ^ lfsr_q[30] ^ data_in[0] ^ data_in[3] ^ data_in[4] ^ data_in[6]; 53 | lfsr_c[27] = lfsr_q[19] ^ lfsr_q[25] ^ lfsr_q[28] ^ lfsr_q[29] ^ lfsr_q[31] ^ data_in[1] ^ data_in[4] ^ data_in[5] ^ data_in[7]; 54 | lfsr_c[28] = lfsr_q[20] ^ lfsr_q[26] ^ lfsr_q[29] ^ lfsr_q[30] ^ data_in[2] ^ data_in[5] ^ data_in[6]; 55 | lfsr_c[29] = lfsr_q[21] ^ lfsr_q[27] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[3] ^ data_in[6] ^ data_in[7]; 56 | lfsr_c[30] = lfsr_q[22] ^ lfsr_q[28] ^ lfsr_q[31] ^ data_in[4] ^ data_in[7]; 57 | lfsr_c[31] = lfsr_q[23] ^ lfsr_q[29] ^ data_in[5]; 58 | 59 | end // always 60 | 61 | always @(posedge clk) begin 62 | if(rst) begin 63 | lfsr_q <= {32{1'b1}}; 64 | end 65 | else begin 66 | lfsr_q <= crc_en ? lfsr_c : lfsr_q; 67 | end 68 | end // always 69 | endmodule // crc 70 | -------------------------------------------------------------------------------- /plinksrc/ponylink_master.v: -------------------------------------------------------------------------------- 1 | // 2 | // PonyLink Chip-to-Chip Interconnect 3 | // 4 | // Copyright (C) 2014 Clifford Wolf 5 | // 6 | // Permission to use, copy, modify, and/or distribute this software for any 7 | // purpose with or without fee is hereby granted, provided that the above 8 | // copyright notice and this permission notice appear in all copies. 9 | // 10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | // 18 | 19 | `timescale 1 ns / 1 ps 20 | 21 | module ponylink_master #( 22 | parameter SERDES_REG_IN = 1, 23 | parameter SERDES_REG_OUT = 1, 24 | parameter M2S_TDATA_WIDTH = 8, 25 | parameter M2S_TUSER_WIDTH = 0, 26 | parameter S2M_TDATA_WIDTH = 8, 27 | parameter S2M_TUSER_WIDTH = 0, 28 | parameter MASTER_RECV_DELAY = 4, 29 | parameter SLAVE_RECV_DELAY = 4, 30 | parameter MASTER_SEND_DELAY = 32, 31 | parameter SLAVE_SEND_DELAY = 32, 32 | parameter MASTER_PARBITS = 1, 33 | parameter SLAVE_PARBITS = 1, 34 | parameter MASTER_PKTLEN = 64, 35 | parameter SLAVE_PKTLEN = 64, 36 | // python timings.py 6 19 0.25 1.5 37 | parameter MASTER_TIMINGS = 80'h1d17100a040f0c090602, 38 | parameter SLAVE_TIMINGS = 80'h05040302010907050301 39 | ) ( 40 | input clk, 41 | input resetn, 42 | output linkerror, 43 | output linkready, 44 | output mode_recv, 45 | output mode_send, 46 | 47 | input [7:0] gpio_i, 48 | output [7:0] gpio_o, 49 | 50 | input [M2S_TDATA_WIDTH-1:0] in_tdata, 51 | input [M2S_TUSER_WIDTH-1:0] in_tuser, 52 | input in_tvalid, 53 | input in_tlast, 54 | output in_tready, 55 | 56 | output [S2M_TDATA_WIDTH-1:0] out_tdata, 57 | output [S2M_TUSER_WIDTH-1:0] out_tuser, 58 | output out_tvalid, 59 | output out_tlast, 60 | input out_tready, 61 | 62 | input [MASTER_PARBITS-1:0] serdes_in, 63 | output [MASTER_PARBITS-1:0] serdes_out, 64 | output [MASTER_PARBITS-1:0] serdes_en 65 | ); 66 | wire resetn_out; 67 | 68 | wire [8:0] in_ser_tdata; 69 | wire in_ser_tvalid, in_ser_tready; 70 | 71 | wire [8:0] out_ser_tdata; 72 | wire out_ser_tvalid, out_ser_tready; 73 | 74 | generate if (M2S_TDATA_WIDTH <= 8 && M2S_TUSER_WIDTH <= 8) begin:pack_8bits 75 | ponylink_pack_8bits #( 76 | .TDATA_WIDTH(M2S_TDATA_WIDTH), 77 | .TUSER_WIDTH(M2S_TUSER_WIDTH) 78 | ) packer ( 79 | .clk(clk), 80 | .resetn(resetn && resetn_out), 81 | .tdata(in_tdata), 82 | .tuser(in_tuser), 83 | .tvalid(in_tvalid), 84 | .tlast(in_tlast), 85 | .tready(in_tready), 86 | .ser_tdata(in_ser_tdata), 87 | .ser_tvalid(in_ser_tvalid), 88 | .ser_tready(in_ser_tready) 89 | ); 90 | end else begin:pack_generic 91 | ponylink_pack_generic #( 92 | .TDATA_WIDTH(M2S_TDATA_WIDTH), 93 | .TUSER_WIDTH(M2S_TUSER_WIDTH) 94 | ) packer ( 95 | .clk(clk), 96 | .resetn(resetn && resetn_out), 97 | .tdata(in_tdata), 98 | .tuser(in_tuser), 99 | .tvalid(in_tvalid), 100 | .tlast(in_tlast), 101 | .tready(in_tready), 102 | .ser_tdata(in_ser_tdata), 103 | .ser_tvalid(in_ser_tvalid), 104 | .ser_tready(in_ser_tready) 105 | ); 106 | end endgenerate 107 | 108 | generate if (S2M_TDATA_WIDTH <= 8 && S2M_TUSER_WIDTH <= 8) begin:unpack_8bits 109 | ponylink_unpack_8bits #( 110 | .TDATA_WIDTH(S2M_TDATA_WIDTH), 111 | .TUSER_WIDTH(S2M_TUSER_WIDTH) 112 | ) unpacker ( 113 | .clk(clk), 114 | .resetn(resetn && resetn_out), 115 | .tdata(out_tdata), 116 | .tuser(out_tuser), 117 | .tvalid(out_tvalid), 118 | .tlast(out_tlast), 119 | .tready(out_tready), 120 | .ser_tdata(out_ser_tdata), 121 | .ser_tvalid(out_ser_tvalid), 122 | .ser_tready(out_ser_tready) 123 | ); 124 | end else begin:unpack_generic 125 | ponylink_unpack_generic #( 126 | .TDATA_WIDTH(S2M_TDATA_WIDTH), 127 | .TUSER_WIDTH(S2M_TUSER_WIDTH) 128 | ) unpacker ( 129 | .clk(clk), 130 | .resetn(resetn && resetn_out), 131 | .tdata(out_tdata), 132 | .tuser(out_tuser), 133 | .tvalid(out_tvalid), 134 | .tlast(out_tlast), 135 | .tready(out_tready), 136 | .ser_tdata(out_ser_tdata), 137 | .ser_tvalid(out_ser_tvalid), 138 | .ser_tready(out_ser_tready) 139 | ); 140 | end endgenerate 141 | 142 | wire [MASTER_PARBITS-1:0] serdes_in_t; 143 | wire [MASTER_PARBITS-1:0] serdes_out_t; 144 | wire [MASTER_PARBITS-1:0] serdes_en_t; 145 | 146 | reg [MASTER_PARBITS-1:0] serdes_in_r; 147 | reg [MASTER_PARBITS-1:0] serdes_out_r; 148 | reg [MASTER_PARBITS-1:0] serdes_en_r; 149 | 150 | ponylink_txrx #( 151 | .RECVRESET(0), 152 | .RECV_DELAY(MASTER_RECV_DELAY), 153 | .SEND_DELAY(MASTER_SEND_DELAY), 154 | .SEND_PKTLEN(MASTER_PKTLEN), 155 | .PARBITS(MASTER_PARBITS), 156 | .TIMINGS(MASTER_TIMINGS) 157 | ) txrx ( 158 | .clk(clk), 159 | .resetn(resetn), 160 | .resetn_out(resetn_out), 161 | .linkerror(linkerror), 162 | .linkready(linkready), 163 | .mode_recv(mode_recv), 164 | .mode_send(mode_send), 165 | 166 | .gpio_i(gpio_i), 167 | .gpio_o(gpio_o), 168 | 169 | .in_ser_tdata(in_ser_tdata), 170 | .in_ser_tvalid(in_ser_tvalid), 171 | .in_ser_tready(in_ser_tready), 172 | 173 | .out_ser_tdata(out_ser_tdata), 174 | .out_ser_tvalid(out_ser_tvalid), 175 | .out_ser_tready(out_ser_tready), 176 | 177 | .serdes_in(serdes_in_t), 178 | .serdes_out(serdes_out_t), 179 | .serdes_en(serdes_en_t) 180 | ); 181 | 182 | generate if (SERDES_REG_IN) begin 183 | always @(posedge clk) serdes_in_r <= serdes_in; 184 | assign serdes_in_t = serdes_in_r; 185 | end else begin 186 | assign serdes_in_t = serdes_in; 187 | end endgenerate 188 | 189 | generate if (SERDES_REG_OUT) begin 190 | always @(posedge clk) serdes_out_r <= serdes_out_t; 191 | always @(posedge clk) serdes_en_r <= serdes_en_t; 192 | assign serdes_out = serdes_out_r, serdes_en = serdes_en_r; 193 | end else begin 194 | assign serdes_out = serdes_out_t, serdes_en = serdes_en_t; 195 | end endgenerate 196 | endmodule 197 | 198 | -------------------------------------------------------------------------------- /plinksrc/ponylink_pack.v: -------------------------------------------------------------------------------- 1 | // 2 | // PonyLink Chip-to-Chip Interconnect 3 | // 4 | // Copyright (C) 2014 Clifford Wolf 5 | // 6 | // Permission to use, copy, modify, and/or distribute this software for any 7 | // purpose with or without fee is hereby granted, provided that the above 8 | // copyright notice and this permission notice appear in all copies. 9 | // 10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | // 18 | 19 | `timescale 1 ns / 1 ps 20 | // `define NO_PONYLINK_SCRAMBLE 21 | 22 | module ponylink_pack_8bits #( 23 | parameter TDATA_WIDTH = 10, 24 | parameter TUSER_WIDTH = 0 25 | )( 26 | input clk, 27 | input resetn, 28 | 29 | input [TDATA_WIDTH-1:0] tdata, 30 | input [TUSER_WIDTH-1:0] tuser, 31 | input tvalid, 32 | input tlast, 33 | output tready, 34 | 35 | output reg [8:0] ser_tdata, 36 | output reg ser_tvalid, 37 | input ser_tready 38 | ); 39 | reg [31:0] rng; 40 | reg [11:0] rng_counter; 41 | reg [4:0] rng_cursor; 42 | 43 | reg [7:0] queue_tuser, queue_tdata; 44 | reg queue_tvalid, queue_tlast, queue_send_tuser, queue_send_tuser2; 45 | assign tready = !queue_tvalid && resetn; 46 | 47 | always @(posedge clk) begin 48 | if (tready && tvalid) begin 49 | queue_tuser <= tuser; 50 | queue_tdata <= tdata; 51 | queue_tlast <= tlast; 52 | queue_send_tuser <= (queue_tuser !== tuser) && (TUSER_WIDTH > 0); 53 | queue_send_tuser2 <= 0; 54 | queue_tvalid <= 1; 55 | end 56 | if (!resetn) begin 57 | `ifdef NO_PONYLINK_SCRAMBLE 58 | rng = 0; 59 | `else 60 | rng = 123456789; 61 | `endif 62 | rng_cursor <= 1; 63 | queue_tuser <= 0; 64 | queue_tlast <= 0; 65 | queue_send_tuser <= 0; 66 | queue_send_tuser2 <= 0; 67 | queue_tvalid <= 0; 68 | ser_tvalid <= 0; 69 | end else if (!ser_tvalid || ser_tready) begin 70 | if (rng_cursor) begin 71 | (* full_case, parallel_case *) 72 | case (1'b1) 73 | rng_cursor[0]: ser_tdata <= 9'h11c; 74 | rng_cursor[1]: ser_tdata <= rng[ 0 +: 8]; 75 | rng_cursor[2]: ser_tdata <= rng[ 8 +: 8]; 76 | rng_cursor[3]: ser_tdata <= rng[16 +: 8]; 77 | rng_cursor[4]: ser_tdata <= rng[24 +: 8]; 78 | endcase 79 | ser_tvalid <= 1; 80 | rng_counter <= 1; 81 | rng_cursor <= rng_cursor << 1; 82 | end else if (queue_tlast) begin 83 | ser_tvalid <= 1; 84 | ser_tdata <= 9'h17c; 85 | queue_tlast <= 0; 86 | end else if (queue_send_tuser) begin 87 | ser_tvalid <= 1; 88 | ser_tdata <= 9'h15c; 89 | queue_send_tuser <= 0; 90 | queue_send_tuser2 <= 1; 91 | end else if (queue_send_tuser2 || queue_tvalid) begin 92 | if (queue_send_tuser2) begin 93 | ser_tvalid <= 1; 94 | ser_tdata <= queue_tuser ^ rng[7:0]; 95 | queue_send_tuser2 <= 0; 96 | end else if (queue_tvalid) begin 97 | ser_tvalid <= 1; 98 | ser_tdata <= queue_tdata ^ rng[7:0]; 99 | queue_tvalid <= 0; 100 | end 101 | rng = rng ^ (rng << 13); 102 | rng = rng ^ (rng >> 17); 103 | rng = rng ^ (rng << 5); 104 | if (!rng_counter) 105 | rng_cursor <= 1; 106 | rng_counter <= rng_counter + 1'b1; 107 | end else 108 | ser_tvalid <= 0; 109 | end 110 | end 111 | endmodule 112 | 113 | module ponylink_unpack_8bits #( 114 | parameter TDATA_WIDTH = 8, 115 | parameter TUSER_WIDTH = 8 116 | ) ( 117 | input clk, 118 | input resetn, 119 | 120 | output reg [TDATA_WIDTH-1:0] tdata, 121 | output reg [TUSER_WIDTH-1:0] tuser, 122 | output reg tvalid, 123 | output reg tlast, 124 | input tready, 125 | 126 | input [8:0] ser_tdata, 127 | input ser_tvalid, 128 | output reg ser_tready 129 | ); 130 | reg store_tuser; 131 | 132 | reg [31:0] rng; 133 | reg [3:0] rng_cursor; 134 | reg rng_next; 135 | 136 | always @(posedge clk) begin 137 | rng_next <= 0; 138 | ser_tready <= 0; 139 | if (tready && tvalid) begin 140 | tlast <= 0; 141 | tvalid <= 0; 142 | end 143 | if (!resetn) begin 144 | rng = 123456789; 145 | rng_cursor <= 0; 146 | rng_next <= 0; 147 | tlast <= 0; 148 | tvalid <= 0; 149 | tuser <= 0; 150 | end else if (!ser_tready && ser_tvalid && (!tvalid || tready)) begin 151 | if (rng_cursor) begin 152 | (* full_case, parallel_case *) 153 | case (1'b1) 154 | rng_cursor[0]: rng[ 0 +: 8] = ser_tdata[7:0]; 155 | rng_cursor[1]: rng[ 8 +: 8] = ser_tdata[7:0]; 156 | rng_cursor[2]: rng[16 +: 8] = ser_tdata[7:0]; 157 | rng_cursor[3]: rng[24 +: 8] = ser_tdata[7:0]; 158 | endcase 159 | rng_cursor <= rng_cursor << 1; 160 | ser_tready <= 1; 161 | end else if (store_tuser) begin 162 | tuser <= ser_tdata[7:0] ^ rng[7:0]; 163 | store_tuser <= 0; 164 | ser_tready <= 1; 165 | rng_next <= 1; 166 | end else if (ser_tdata == 9'h17c) begin 167 | tlast <= 1; 168 | ser_tready <= 1; 169 | end else if (ser_tdata == 9'h15c) begin 170 | store_tuser <= 1; 171 | ser_tready <= 1; 172 | end else if (ser_tdata == 9'h11c) begin 173 | rng_cursor <= 1; 174 | ser_tready <= 1; 175 | end else begin 176 | tdata <= ser_tdata[7:0] ^ rng[7:0]; 177 | tvalid <= 1; 178 | ser_tready <= 1; 179 | rng_next <= 1; 180 | end 181 | end else if (rng_next) begin 182 | rng = rng ^ (rng << 13); 183 | rng = rng ^ (rng >> 17); 184 | rng = rng ^ (rng << 5); 185 | end 186 | end 187 | endmodule 188 | 189 | module ponylink_pack_generic #( 190 | parameter TDATA_WIDTH = 10, 191 | parameter TUSER_WIDTH = 0 192 | ) ( 193 | input clk, 194 | input resetn, 195 | 196 | input [TDATA_WIDTH-1:0] tdata, 197 | input [TUSER_WIDTH-1:0] tuser, 198 | input tvalid, 199 | input tlast, 200 | output reg tready, 201 | 202 | output reg [8:0] ser_tdata, 203 | output reg ser_tvalid, 204 | input ser_tready 205 | ); 206 | localparam TDATA_BYTES = (TDATA_WIDTH + 7) / 8; 207 | localparam TUSER_BYTES = (TUSER_WIDTH + 7) / 8; 208 | 209 | reg [TDATA_BYTES-1:0] cursor_td; 210 | reg [TUSER_BYTES-1:0] cursor_tu; 211 | reg [8*TDATA_BYTES-1:0] current_td; 212 | reg [8*TUSER_BYTES-1:0] current_tu; 213 | reg current_tl, mkseq_tu; 214 | 215 | integer i; 216 | reg [31:0] rng; 217 | reg [11:0] rng_counter; 218 | reg [4:0] rng_cursor; 219 | 220 | reg [TDATA_BYTES-1:0] nxt_cursor_td; 221 | reg [TUSER_BYTES-1:0] nxt_cursor_tu; 222 | reg nxt_current_tl, nxt_mkseq_tu; 223 | 224 | always @(posedge clk) begin 225 | ser_tdata <= 'bx; 226 | ser_tvalid <= 0; 227 | tready <= 0; 228 | if (!resetn) begin 229 | cursor_td = 0; 230 | cursor_tu = 0; 231 | current_tu = 0; 232 | current_tl = 0; 233 | mkseq_tu = 0; 234 | nxt_cursor_td = 0; 235 | nxt_cursor_tu = 0; 236 | nxt_current_tl = 0; 237 | nxt_mkseq_tu = 0; 238 | `ifdef NO_PONYLINK_SCRAMBLE 239 | rng = 0; 240 | `else 241 | rng = 123456789; 242 | `endif 243 | rng_counter = 1; 244 | rng_cursor = 1; 245 | end else begin 246 | if (ser_tvalid && ser_tready) begin 247 | if (!current_tl && !mkseq_tu && !rng_cursor) begin 248 | rng = rng ^ (rng << 13); 249 | rng = rng ^ (rng >> 17); 250 | rng = rng ^ (rng << 5); 251 | if (!rng_counter) 252 | rng_cursor = 1; 253 | rng_counter = rng_counter+1; 254 | end else 255 | rng_cursor = rng_cursor << 1; 256 | cursor_td = nxt_cursor_td; 257 | cursor_tu = nxt_cursor_tu; 258 | current_tl = nxt_current_tl; 259 | mkseq_tu = nxt_mkseq_tu; 260 | end 261 | if (tvalid && tready) begin 262 | cursor_td = 1; 263 | cursor_tu = (current_tu === tuser || TUSER_WIDTH == 0) ? 0 : 1; 264 | mkseq_tu = (current_tu === tuser || TUSER_WIDTH == 0) ? 0 : 1; 265 | current_td = tdata; 266 | current_tu = tuser; 267 | current_tl = tlast; 268 | 269 | nxt_cursor_td = cursor_td; 270 | nxt_cursor_tu = cursor_tu; 271 | nxt_current_tl = current_tl; 272 | nxt_mkseq_tu = mkseq_tu; 273 | end 274 | if (rng_cursor) begin 275 | if (rng_cursor[0]) ser_tdata <= 9'h11c; 276 | if (rng_cursor[1]) ser_tdata <= rng[ 0 +: 8]; 277 | if (rng_cursor[2]) ser_tdata <= rng[ 8 +: 8]; 278 | if (rng_cursor[3]) ser_tdata <= rng[16 +: 8]; 279 | if (rng_cursor[4]) ser_tdata <= rng[24 +: 8]; 280 | ser_tvalid <= 1; 281 | end else 282 | if (current_tl) begin 283 | ser_tdata <= 9'h17c; 284 | ser_tvalid <= 1; 285 | nxt_current_tl = 0; 286 | end else 287 | if (mkseq_tu) begin 288 | ser_tdata <= 9'h15c; 289 | ser_tvalid <= 1; 290 | nxt_mkseq_tu = 0; 291 | end else 292 | if (cursor_tu) begin 293 | for (i = 0; i < TUSER_BYTES; i = i+1) 294 | if (cursor_tu[i]) begin ser_tdata <= current_tu[8*i +: 8] ^ rng[7:0]; ser_tvalid <= 1; end 295 | nxt_cursor_tu = cursor_tu << 1; 296 | end else 297 | if (cursor_td) begin 298 | for (i = 0; i < TDATA_BYTES; i = i+1) 299 | if (cursor_td[i]) begin ser_tdata <= current_td[8*i +: 8] ^ rng[7:0]; ser_tvalid <= 1; end 300 | nxt_cursor_td = cursor_td << 1; 301 | end else 302 | tready <= 1; 303 | end 304 | end 305 | endmodule 306 | 307 | module ponylink_unpack_generic #( 308 | parameter TDATA_WIDTH = 10, 309 | parameter TUSER_WIDTH = 0 310 | ) ( 311 | input clk, 312 | input resetn, 313 | 314 | output [TDATA_WIDTH-1:0] tdata, 315 | output [TUSER_WIDTH-1:0] tuser, 316 | output tvalid, 317 | output tlast, 318 | input tready, 319 | 320 | input [8:0] ser_tdata, 321 | input ser_tvalid, 322 | output reg ser_tready 323 | ); 324 | reg [TDATA_WIDTH-1:0] buffer_td [0:7]; 325 | reg [TUSER_WIDTH-1:0] buffer_tu [0:7]; 326 | reg buffer_tl [0:7]; 327 | reg [2:0] buffer_in, buffer_out; 328 | 329 | localparam TDATA_BYTES = (TDATA_WIDTH + 7) / 8; 330 | localparam TUSER_BYTES = (TUSER_WIDTH + 7) / 8; 331 | 332 | reg [TDATA_BYTES-1:0] cursor_td; 333 | reg [TUSER_BYTES-1:0] cursor_tu; 334 | reg [8*TDATA_BYTES-1:0] next_td; 335 | reg [8*TUSER_BYTES-1:0] next_tu; 336 | reg next_tl; 337 | 338 | integer i; 339 | reg [31:0] rng; 340 | reg [3:0] rng_cursor; 341 | reg reset_q; 342 | 343 | assign tdata = buffer_td[buffer_out]; 344 | assign tuser = buffer_tu[buffer_out]; 345 | assign tlast = buffer_tl[buffer_out]; 346 | assign tvalid = buffer_in != buffer_out && resetn; 347 | 348 | always @(posedge clk) begin 349 | reset_q <= !resetn; 350 | if (!resetn) begin 351 | buffer_in <= 0; 352 | buffer_out <= 0; 353 | ser_tready <= 0; 354 | 355 | cursor_td = 1; 356 | cursor_tu = 0; 357 | next_tl = 0; 358 | next_tu = 0; 359 | rng = 0; 360 | rng_cursor = 0; 361 | end else begin 362 | if (!cursor_td && !cursor_tu) begin 363 | buffer_td[buffer_in] <= next_td; 364 | buffer_tu[buffer_in] <= next_tu; 365 | buffer_tl[buffer_in] <= next_tl; 366 | buffer_in <= buffer_in + 1; 367 | ser_tready <= (buffer_out - buffer_in) > 3'd3; 368 | 369 | cursor_td = 1; 370 | next_tl = 0; 371 | end 372 | if (ser_tvalid && ser_tready) begin 373 | if (ser_tdata == 9'h11c) begin 374 | rng_cursor = 1; 375 | end else 376 | if (ser_tdata == 9'h15c && TUSER_BYTES) begin 377 | cursor_tu = 1; 378 | end else 379 | if (ser_tdata == 9'h17c) begin 380 | next_tl = 1; 381 | end else 382 | if (rng_cursor) begin 383 | if (rng_cursor[0]) rng[ 0 +: 8] = ser_tdata; 384 | if (rng_cursor[1]) rng[ 8 +: 8] = ser_tdata; 385 | if (rng_cursor[2]) rng[16 +: 8] = ser_tdata; 386 | if (rng_cursor[3]) rng[24 +: 8] = ser_tdata; 387 | rng_cursor = rng_cursor << 1; 388 | end else begin 389 | for (i = 0; i < TUSER_BYTES; i = i+1) 390 | if (cursor_tu[i]) next_tu[8*i +: 8] = ser_tdata ^ rng[7:0]; 391 | for (i = 0; i < TDATA_BYTES; i = i+1) 392 | if (cursor_td[i]) next_td[8*i +: 8] = ser_tdata ^ rng[7:0]; 393 | if (cursor_tu && TUSER_BYTES) 394 | cursor_tu = cursor_tu << 1; 395 | else 396 | cursor_td = cursor_td << 1; 397 | rng = rng ^ (rng << 13); 398 | rng = rng ^ (rng >> 17); 399 | rng = rng ^ (rng << 5); 400 | end 401 | end 402 | if (tvalid && tready) begin 403 | buffer_out <= buffer_out + 1; 404 | ser_tready <= 1; 405 | end 406 | if (reset_q) 407 | ser_tready <= 1; 408 | end 409 | end 410 | endmodule 411 | 412 | -------------------------------------------------------------------------------- /plinksrc/ponylink_slave.v: -------------------------------------------------------------------------------- 1 | // 2 | // PonyLink Chip-to-Chip Interconnect 3 | // 4 | // Copyright (C) 2014 Clifford Wolf 5 | // 6 | // Permission to use, copy, modify, and/or distribute this software for any 7 | // purpose with or without fee is hereby granted, provided that the above 8 | // copyright notice and this permission notice appear in all copies. 9 | // 10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | // 18 | 19 | `timescale 1 ns / 1 ps 20 | 21 | module ponylink_slave #( 22 | parameter SERDES_REG_IN = 1, 23 | parameter SERDES_REG_OUT = 1, 24 | parameter M2S_TDATA_WIDTH = 8, 25 | parameter M2S_TUSER_WIDTH = 0, 26 | parameter S2M_TDATA_WIDTH = 8, 27 | parameter S2M_TUSER_WIDTH = 0, 28 | parameter MASTER_RECV_DELAY = 4, 29 | parameter SLAVE_RECV_DELAY = 4, 30 | parameter MASTER_SEND_DELAY = 32, 31 | parameter SLAVE_SEND_DELAY = 32, 32 | parameter MASTER_PARBITS = 1, 33 | parameter SLAVE_PARBITS = 1, 34 | parameter MASTER_PKTLEN = 64, 35 | parameter SLAVE_PKTLEN = 64, 36 | // python timings.py 6 19 0.25 1.5 37 | parameter MASTER_TIMINGS = 80'h1d17100a040f0c090602, 38 | parameter SLAVE_TIMINGS = 80'h05040302010907050301 39 | ) ( 40 | input clk, 41 | output resetn_out, 42 | output linkerror, 43 | output linkready, 44 | output mode_recv, 45 | output mode_send, 46 | 47 | input [7:0] gpio_i, 48 | output [7:0] gpio_o, 49 | 50 | input [S2M_TDATA_WIDTH-1:0] in_tdata, 51 | input [S2M_TUSER_WIDTH-1:0] in_tuser, 52 | input in_tvalid, 53 | input in_tlast, 54 | output in_tready, 55 | 56 | output [M2S_TDATA_WIDTH-1:0] out_tdata, 57 | output [M2S_TUSER_WIDTH-1:0] out_tuser, 58 | output out_tvalid, 59 | output out_tlast, 60 | input out_tready, 61 | 62 | input [SLAVE_PARBITS-1:0] serdes_in, 63 | output [SLAVE_PARBITS-1:0] serdes_out, 64 | output [SLAVE_PARBITS-1:0] serdes_en 65 | ); 66 | wire [8:0] in_ser_tdata; 67 | wire in_ser_tvalid, in_ser_tready; 68 | 69 | wire [8:0] out_ser_tdata; 70 | wire out_ser_tvalid, out_ser_tready; 71 | 72 | generate if (S2M_TDATA_WIDTH <= 8 && S2M_TUSER_WIDTH <= 8) begin:pack_8bits 73 | ponylink_pack_8bits #( 74 | .TDATA_WIDTH(S2M_TDATA_WIDTH), 75 | .TUSER_WIDTH(S2M_TUSER_WIDTH) 76 | ) packer ( 77 | .clk(clk), 78 | .resetn(resetn_out), 79 | .tdata(in_tdata), 80 | .tuser(in_tuser), 81 | .tvalid(in_tvalid), 82 | .tlast(in_tlast), 83 | .tready(in_tready), 84 | .ser_tdata(in_ser_tdata), 85 | .ser_tvalid(in_ser_tvalid), 86 | .ser_tready(in_ser_tready) 87 | ); 88 | end else begin:pack_generic 89 | ponylink_pack_generic #( 90 | .TDATA_WIDTH(S2M_TDATA_WIDTH), 91 | .TUSER_WIDTH(S2M_TUSER_WIDTH) 92 | ) packer ( 93 | .clk(clk), 94 | .resetn(resetn_out), 95 | .tdata(in_tdata), 96 | .tuser(in_tuser), 97 | .tvalid(in_tvalid), 98 | .tlast(in_tlast), 99 | .tready(in_tready), 100 | .ser_tdata(in_ser_tdata), 101 | .ser_tvalid(in_ser_tvalid), 102 | .ser_tready(in_ser_tready) 103 | ); 104 | end endgenerate 105 | 106 | generate if (M2S_TDATA_WIDTH <= 8 && M2S_TUSER_WIDTH <= 8) begin:unpack_8bits 107 | ponylink_unpack_8bits #( 108 | .TDATA_WIDTH(M2S_TDATA_WIDTH), 109 | .TUSER_WIDTH(M2S_TUSER_WIDTH) 110 | ) unpacker ( 111 | .clk(clk), 112 | .resetn(resetn_out), 113 | .tdata(out_tdata), 114 | .tuser(out_tuser), 115 | .tvalid(out_tvalid), 116 | .tlast(out_tlast), 117 | .tready(out_tready), 118 | .ser_tdata(out_ser_tdata), 119 | .ser_tvalid(out_ser_tvalid), 120 | .ser_tready(out_ser_tready) 121 | ); 122 | end else begin:unpack_generic 123 | ponylink_unpack_generic #( 124 | .TDATA_WIDTH(M2S_TDATA_WIDTH), 125 | .TUSER_WIDTH(M2S_TUSER_WIDTH) 126 | ) unpacker ( 127 | .clk(clk), 128 | .resetn(resetn_out), 129 | .tdata(out_tdata), 130 | .tuser(out_tuser), 131 | .tvalid(out_tvalid), 132 | .tlast(out_tlast), 133 | .tready(out_tready), 134 | .ser_tdata(out_ser_tdata), 135 | .ser_tvalid(out_ser_tvalid), 136 | .ser_tready(out_ser_tready) 137 | ); 138 | end endgenerate 139 | 140 | wire [SLAVE_PARBITS-1:0] serdes_in_t; 141 | wire [SLAVE_PARBITS-1:0] serdes_out_t; 142 | wire [SLAVE_PARBITS-1:0] serdes_en_t; 143 | 144 | reg [SLAVE_PARBITS-1:0] serdes_in_r; 145 | reg [SLAVE_PARBITS-1:0] serdes_out_r; 146 | reg [SLAVE_PARBITS-1:0] serdes_en_r; 147 | 148 | ponylink_txrx #( 149 | .RECVRESET(1), 150 | .RECV_DELAY(SLAVE_RECV_DELAY), 151 | .SEND_DELAY(SLAVE_SEND_DELAY), 152 | .SEND_PKTLEN(SLAVE_PKTLEN), 153 | .PARBITS(SLAVE_PARBITS), 154 | .TIMINGS(SLAVE_TIMINGS) 155 | ) txrx ( 156 | .clk(clk), 157 | .resetn(1'b1), 158 | .resetn_out(resetn_out), 159 | .linkerror(linkerror), 160 | .linkready(linkready), 161 | .mode_recv(mode_recv), 162 | .mode_send(mode_send), 163 | 164 | .gpio_i(gpio_i), 165 | .gpio_o(gpio_o), 166 | 167 | .in_ser_tdata(in_ser_tdata), 168 | .in_ser_tvalid(in_ser_tvalid), 169 | .in_ser_tready(in_ser_tready), 170 | 171 | .out_ser_tdata(out_ser_tdata), 172 | .out_ser_tvalid(out_ser_tvalid), 173 | .out_ser_tready(out_ser_tready), 174 | 175 | .serdes_in(serdes_in_t), 176 | .serdes_out(serdes_out_t), 177 | .serdes_en(serdes_en_t) 178 | ); 179 | 180 | generate if (SERDES_REG_IN) begin 181 | always @(posedge clk) serdes_in_r <= serdes_in; 182 | assign serdes_in_t = serdes_in_r; 183 | end else begin 184 | assign serdes_in_t = serdes_in; 185 | end endgenerate 186 | 187 | generate if (SERDES_REG_OUT) begin 188 | always @(posedge clk) serdes_out_r <= serdes_out_t; 189 | always @(posedge clk) serdes_en_r <= serdes_en_t; 190 | assign serdes_out = serdes_out_r, serdes_en = serdes_en_r; 191 | end else begin 192 | assign serdes_out = serdes_out_t, serdes_en = serdes_en_t; 193 | end endgenerate 194 | endmodule 195 | 196 | -------------------------------------------------------------------------------- /plinksrc/ponylink_test.v: -------------------------------------------------------------------------------- 1 | // 2 | // PonyLink Chip-to-Chip Interconnect 3 | // 4 | // Copyright (C) 2014 Clifford Wolf 5 | // 6 | // Permission to use, copy, modify, and/or distribute this software for any 7 | // purpose with or without fee is hereby granted, provided that the above 8 | // copyright notice and this permission notice appear in all copies. 9 | // 10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | // 18 | 19 | `timescale 1 ns / 1 ps 20 | 21 | module ponylink_test #( 22 | parameter M2S_TDATA_WIDTH = 8, 23 | parameter M2S_TUSER_WIDTH = 0, 24 | parameter S2M_TDATA_WIDTH = 8, 25 | parameter S2M_TUSER_WIDTH = 0, 26 | parameter MASTER_PARBITS = 2, 27 | parameter SLAVE_PARBITS = 2, 28 | parameter MASTER_TIMINGS = 80'h0e0b0805020d0a070401, 29 | parameter SLAVE_TIMINGS = 80'h0e0b0805020d0a070401, 30 | 31 | parameter real MASTER_BIT_PERIOD_NS = 10.0, 32 | parameter real MASTER_PULSE_JITTER_NS = 0.0, 33 | parameter real SLAVE_BIT_PERIOD_NS = 10.0, 34 | parameter real SLAVE_PULSE_JITTER_NS = 0.0 35 | ) ( 36 | input master_clk, 37 | input master_resetn, 38 | output master_linkerror, 39 | output master_linkready, 40 | 41 | input slave_clk, 42 | output slave_resetn, 43 | output slave_linkerror, 44 | output slave_linkready, 45 | 46 | input [7:0] master_gpio_i, 47 | output [7:0] master_gpio_o, 48 | 49 | input [7:0] slave_gpio_i, 50 | output [7:0] slave_gpio_o, 51 | 52 | input [M2S_TDATA_WIDTH-1:0] master_in_tdata, 53 | input [M2S_TUSER_WIDTH-1:0] master_in_tuser, 54 | input master_in_tvalid, 55 | input master_in_tlast, 56 | output master_in_tready, 57 | 58 | output [S2M_TDATA_WIDTH-1:0] master_out_tdata, 59 | output [S2M_TUSER_WIDTH-1:0] master_out_tuser, 60 | output master_out_tvalid, 61 | output master_out_tlast, 62 | input master_out_tready, 63 | 64 | input [S2M_TDATA_WIDTH-1:0] slave_in_tdata, 65 | input [S2M_TUSER_WIDTH-1:0] slave_in_tuser, 66 | input slave_in_tvalid, 67 | input slave_in_tlast, 68 | output slave_in_tready, 69 | 70 | output [M2S_TDATA_WIDTH-1:0] slave_out_tdata, 71 | output [M2S_TUSER_WIDTH-1:0] slave_out_tuser, 72 | output slave_out_tvalid, 73 | output slave_out_tlast, 74 | input slave_out_tready, 75 | 76 | input link_scramble, 77 | input link_scramble_idle, 78 | output link_collision, 79 | 80 | output master_recv, 81 | output master_send, 82 | output slave_recv, 83 | output slave_send 84 | ); 85 | reg link_collision; 86 | reg link_master, link_slave, link_signal; 87 | reg [31:0] link_rng = 1; 88 | 89 | always @(link_master, link_slave, master_clk, slave_clk) begin 90 | link_collision = 0; 91 | if (link_scramble || (link_master === 1'bz && link_slave === 1'bz)) begin 92 | if (link_scramble || link_scramble_idle) begin 93 | link_signal = link_rng; 94 | link_rng = link_rng ^ (link_rng << 13); 95 | link_rng = link_rng ^ (link_rng >> 7); 96 | link_rng = link_rng ^ (link_rng << 17); 97 | end 98 | end else if (link_master === 1'bz && link_slave !== 1'bz) 99 | link_signal = link_slave; 100 | else if (link_master !== 1'bz && link_slave === 1'bz) 101 | link_signal = link_master; 102 | else begin 103 | link_signal = link_master === link_slave ? link_master : 1'bx; 104 | link_collision = 1; 105 | end 106 | end 107 | 108 | event send_master, send_slave; 109 | event sample_master, sample_slave; 110 | 111 | integer master_bitcnt, slave_bitcnt; 112 | real master_jitter, slave_jitter; 113 | 114 | reg [MASTER_PARBITS-1:0] master_serdes_in; 115 | wire [MASTER_PARBITS-1:0] master_serdes_out; 116 | wire [MASTER_PARBITS-1:0] master_serdes_en; 117 | 118 | always @(posedge master_clk) begin:master_ser 119 | reg [MASTER_PARBITS-1:0] bits, en; 120 | real bit_time, delta_bit_time; 121 | reg last_bit; 122 | integer i; 123 | 124 | bits = master_serdes_out; 125 | en = master_serdes_en; 126 | 127 | bit_time = MASTER_BIT_PERIOD_NS * 0.5; 128 | 129 | for (i = 0; i < MASTER_PARBITS; i = i+1) begin 130 | delta_bit_time = 0; 131 | if (en[i]) begin 132 | if (last_bit !== bits[i]) begin 133 | master_jitter = (MASTER_PULSE_JITTER_NS * 0.01) * ($random % 45); 134 | bit_time = bit_time + master_jitter; 135 | delta_bit_time = -master_jitter; 136 | end 137 | last_bit = bits[i]; 138 | end else 139 | last_bit = 'bx; 140 | if (bit_time <= 0 || bit_time >= 1.5 * MASTER_BIT_PERIOD_NS) begin 141 | $display("Out-of-bounds master bit time: %.3f (valid range = 0 .. %.3f)", bit_time, 1.5 * MASTER_BIT_PERIOD_NS); 142 | #(10 * MASTER_BIT_PERIOD_NS); 143 | $stop; 144 | end 145 | #(bit_time); 146 | 147 | -> send_master; 148 | link_master = en[i] ? bits[i] : 1'bz; 149 | bit_time = MASTER_BIT_PERIOD_NS + delta_bit_time; 150 | end 151 | end 152 | 153 | always @(posedge master_clk) begin:master_des 154 | reg [MASTER_PARBITS-1:0] bits; 155 | reg last_bit; 156 | integer i; 157 | 158 | master_serdes_in <= bits; 159 | for (i = 0; i < MASTER_PARBITS; i = i+1) begin 160 | if (i > 0) 161 | #(MASTER_BIT_PERIOD_NS); 162 | if (link_signal == last_bit) 163 | master_bitcnt = master_bitcnt + 1; 164 | else 165 | master_bitcnt = 1; 166 | bits[i] = link_signal; 167 | last_bit = link_signal; 168 | -> sample_master; 169 | end 170 | end 171 | 172 | reg [SLAVE_PARBITS-1:0] slave_serdes_in; 173 | wire [SLAVE_PARBITS-1:0] slave_serdes_out; 174 | wire [SLAVE_PARBITS-1:0] slave_serdes_en; 175 | 176 | always @(posedge slave_clk) begin:slave_ser 177 | reg [SLAVE_PARBITS-1:0] bits, en; 178 | real bit_time, delta_bit_time; 179 | reg last_bit; 180 | integer i; 181 | 182 | bits = slave_serdes_out; 183 | en = slave_serdes_en; 184 | 185 | bit_time = SLAVE_BIT_PERIOD_NS * 0.5; 186 | 187 | for (i = 0; i < SLAVE_PARBITS; i = i+1) begin 188 | delta_bit_time = 0; 189 | if (en[i]) begin 190 | if (last_bit !== bits[i]) begin 191 | slave_jitter = (SLAVE_PULSE_JITTER_NS * 0.01) * ($random % 45); 192 | bit_time = bit_time + slave_jitter; 193 | delta_bit_time = -slave_jitter; 194 | end 195 | last_bit = bits[i]; 196 | end else 197 | last_bit = 'bx; 198 | if (bit_time <= 0 || bit_time >= 1.5 * SLAVE_BIT_PERIOD_NS) begin 199 | $display("Out-of-bounds slave bit time: %.3f (valid range = 0 .. %.3f)", bit_time, 1.5 * SLAVE_BIT_PERIOD_NS); 200 | #(10 * SLAVE_BIT_PERIOD_NS); 201 | $stop; 202 | end 203 | #(bit_time); 204 | 205 | -> send_slave; 206 | link_slave = en[i] ? bits[i] : 1'bz; 207 | bit_time = SLAVE_BIT_PERIOD_NS + delta_bit_time; 208 | end 209 | end 210 | 211 | always @(posedge slave_clk) begin:slave_des 212 | reg [SLAVE_PARBITS-1:0] bits; 213 | reg last_bit; 214 | integer i; 215 | 216 | slave_serdes_in <= bits; 217 | for (i = 0; i < SLAVE_PARBITS; i = i+1) begin 218 | if (i > 0) 219 | #(SLAVE_BIT_PERIOD_NS); 220 | if (link_signal == last_bit) 221 | slave_bitcnt = slave_bitcnt + 1; 222 | else 223 | slave_bitcnt = 1; 224 | bits[i] = link_signal; 225 | last_bit = link_signal; 226 | -> sample_slave; 227 | end 228 | end 229 | 230 | ponylink_master #( 231 | .M2S_TDATA_WIDTH(M2S_TDATA_WIDTH), 232 | .M2S_TUSER_WIDTH(M2S_TUSER_WIDTH), 233 | .S2M_TDATA_WIDTH(S2M_TDATA_WIDTH), 234 | .S2M_TUSER_WIDTH(S2M_TUSER_WIDTH), 235 | .MASTER_PARBITS(MASTER_PARBITS), 236 | .MASTER_TIMINGS(MASTER_TIMINGS), 237 | .SLAVE_PARBITS(SLAVE_PARBITS), 238 | .SLAVE_TIMINGS(SLAVE_TIMINGS) 239 | ) test_master ( 240 | .clk(master_clk), 241 | .resetn(master_resetn), 242 | .linkerror(master_linkerror), 243 | .linkready(master_linkready), 244 | .mode_recv(master_recv), 245 | .mode_send(master_send), 246 | 247 | .gpio_i(master_gpio_i), 248 | .gpio_o(master_gpio_o), 249 | 250 | .in_tdata(master_in_tdata), 251 | .in_tuser(master_in_tuser), 252 | .in_tlast(master_in_tlast), 253 | .in_tvalid(master_in_tvalid), 254 | .in_tready(master_in_tready), 255 | 256 | .out_tdata(master_out_tdata), 257 | .out_tuser(master_out_tuser), 258 | .out_tlast(master_out_tlast), 259 | .out_tvalid(master_out_tvalid), 260 | .out_tready(master_out_tready), 261 | 262 | .serdes_in(master_serdes_in), 263 | .serdes_out(master_serdes_out), 264 | .serdes_en(master_serdes_en) 265 | ); 266 | 267 | ponylink_slave #( 268 | .M2S_TDATA_WIDTH(M2S_TDATA_WIDTH), 269 | .M2S_TUSER_WIDTH(M2S_TUSER_WIDTH), 270 | .S2M_TDATA_WIDTH(S2M_TDATA_WIDTH), 271 | .S2M_TUSER_WIDTH(S2M_TUSER_WIDTH), 272 | .MASTER_PARBITS(MASTER_PARBITS), 273 | .MASTER_TIMINGS(MASTER_TIMINGS), 274 | .SLAVE_PARBITS(SLAVE_PARBITS), 275 | .SLAVE_TIMINGS(SLAVE_TIMINGS) 276 | ) test_slave ( 277 | .clk(slave_clk), 278 | .resetn_out(slave_resetn), 279 | .linkerror(slave_linkerror), 280 | .linkready(slave_linkready), 281 | .mode_recv(slave_recv), 282 | .mode_send(slave_send), 283 | 284 | .gpio_i(slave_gpio_i), 285 | .gpio_o(slave_gpio_o), 286 | 287 | .in_tdata(slave_in_tdata), 288 | .in_tuser(slave_in_tuser), 289 | .in_tlast(slave_in_tlast), 290 | .in_tvalid(slave_in_tvalid), 291 | .in_tready(slave_in_tready), 292 | 293 | .out_tdata(slave_out_tdata), 294 | .out_tuser(slave_out_tuser), 295 | .out_tlast(slave_out_tlast), 296 | .out_tvalid(slave_out_tvalid), 297 | .out_tready(slave_out_tready), 298 | 299 | .serdes_in(slave_serdes_in), 300 | .serdes_out(slave_serdes_out), 301 | .serdes_en(slave_serdes_en) 302 | ); 303 | endmodule 304 | 305 | -------------------------------------------------------------------------------- /plinksrc/protocol.txt: -------------------------------------------------------------------------------- 1 | 2 | Protocol and IP Internals 3 | ========================= 4 | 5 | The folowing sections describe the protocol internals and how the IP implements 6 | the protocol. 7 | 8 | 9 | GPIO Port 10 | --------- 11 | 12 | The GPIO port provide 8 inputs and 8 output on each side. This interface is 13 | asynchonous. I.e. it should only be used for slowly changing signals. A stall 14 | in the AXIS interfaces will not cause GPIO status updates to be blocked from 15 | being transmitted over the link. 16 | 17 | 18 | AXIS Input and Output 19 | --------------------- 20 | 21 | PonyLink provides a pair of AXIS (AXI Stream) interfaces for data transfers from 22 | the Master to the Slave and a pair of AXIS interfaces for the other direction. Both 23 | directions feature the following AXIS signals: 24 | 25 | TDATA, TUSER, TLAST, TVALID, TREADY 26 | 27 | Each direction has independent TDATA_WIDTH and TUSER_WIDTH. Internally the 28 | TDATA_WIDTH and TUSER_WIDTH are extended to a multiple of 8 bits. 29 | 30 | The best performance can be archieved if TDATA_WIDTH and TUSER_WIDTH 31 | are both less or equal 8 bits. 32 | 33 | The encoding algorithm is most efficient if the TUSER bits only toggle 34 | infrequently. It is recommended to move all frequently toggling bits from 35 | TUSER to TDATA (and all infrequently toggling bits from TDATA to TUSER) 36 | for best efficiency. 37 | 38 | 39 | Pack and Unpack 40 | --------------- 41 | 42 | The packer transforms the stream from the input AXIS interface into a stream of 43 | 8b/10b symbols (8 bit data words and the occational control symbol, encoded as 44 | 9 bit words with the MSB cleared for data words). The unpacker reverts this 45 | process. 46 | 47 | For packing the TDATA and TUSER widths are padded to a multiple of 8 bits. 48 | The data is sent LSB first. The following control symbols are used: 49 | 50 | 0x11c (K.28.0) -- sync 51 | 52 | This paket indicates the start of a TDATA word and is 53 | inserted about once every 4k symbols. It also resets the 54 | RNG used for scrambling. The following four bytes are a 55 | seed value for the RNG. 56 | 57 | 0x15c (K.28.2) -- set TUSER 58 | 59 | The following bytes contain a new value for the TUSER 60 | field. The new TUSER field is used for all subsequent 61 | words until a new TUSER value is transmitted. 62 | 63 | 0x17c (K.28.3) -- set TLAST 64 | 65 | Set the TLAST bit in this cycle. 66 | 67 | 68 | Scramble and Unscramble 69 | ----------------------- 70 | 71 | The scrambler is using a pseudo RNG to create a stream of 8 bit words. Whenever 72 | a non-control symbol is passed through the scrambler, it is XOR-combined with 73 | the next word from the RNG. 74 | 75 | The unscrambler is using a matched RNG to recover the original data. A link 76 | reset event will reset the RNGs in all scramble and unscramble units. 77 | 78 | TBD: The exact properties of the RNG are unspecified at this moment. 79 | 80 | 81 | TX/RX Engine 82 | ------------ 83 | 84 | All bus activity is initiated by the master side of the link. One transfer 85 | consits of one packet of variable length being sent from the master to the 86 | slave, and one response packet of variable length being sent from the slave 87 | back to the master. Both packet follow the same format: 88 | 89 | 1. Preamble 90 | the byte 0xb5 followed by the 0x1fc (K.28.7) control 91 | symbol (K.28.7 is a comma symbol), followed by 0x9e for 92 | packets from the master to the slave, or 0x74 for 93 | packets from the slave to the master. 94 | 95 | The 8b10b running disparity is initialized to -1. 96 | 97 | 2. Sequence control 98 | the sequence-id of the last payload symbol recieved from the 99 | peer, followed by the sequence-id of the first word in the 100 | payload part of this packet. 101 | 102 | 3. Payload 103 | Up to 64 payload symbols 104 | 105 | 4. Optional GPIO update 106 | If there has been a change to the GPIO input bits on this 107 | side, insert the control symbol 0x19c (K.28.4) followed by 108 | an (unscrambled) data symbol representing the new GPIO 109 | input values. 110 | 111 | 5. End Symbol and Checksum 112 | Finally the 0x1bc (K.28.5) control symbol followed by a 113 | 32 bit checksum (CRC-32) followed by at least one additional 114 | transition on the signal line. 115 | 116 | Sequence IDs: 117 | 118 | Each payload word has a (ascending) 8 bit sequence number assigned to it. This 119 | sequence numbers are used for resending data after a transmission error and for 120 | flow control. The sender will only discard data from its send buffer once it 121 | recevied an acknowledgement from the reciever for the data (first word in 122 | sequence control). Data that has not been acknowledged will be resent. Data 123 | that has been recieved for the second time will be ignored. 124 | 125 | Error handling: 126 | 127 | When a 0x1fc (K.28.7) control symbol is recieved then all previously recieved 128 | data that presumably belonged to a packet is discarded. When the checksum for a 129 | packet shows a transmission error (or there is a 8b10b decode error, or a 8b10b 130 | disparity error or there was no end symbol within the expected region) then the 131 | packet is discarded (as if it was never recieved). 132 | 133 | The master will wait for at least the maximum packet size for a preamble from 134 | the slave after the master packet has been sent. If no preamble from the slave 135 | has been recieved until then, the master will resend his packet. 136 | 137 | If the slave is physically capable of detecting a collision on the transmission 138 | media, then a collision will cause the slave to stop sending data. 139 | 140 | Reset event: 141 | 142 | The master can send a reset sequence to the slave to trigger a slave reset 143 | (automatically done when the master is reset using resetn). The slave 144 | responds with a modified reset sequence. The master core keeps sending the 145 | reset signal to the slave until it recieves the proper response. 146 | 147 | The reset sequence is: the byte 0xb5 followed by the 0x1fc (K.28.7) control 148 | symbol, followed by four times the control symbol 0x1fd (K.29.7) or 0x1fe 149 | (K.30.7) for the master or slave respectively, followed by the data bytes 150 | 4, 5, 6, ..., 13, 14. The 8b10b running disparity is initialized to -1. 151 | 152 | Spread-spectrum idle behavior: 153 | 154 | If the master sends less than 64 payload symbols to the slave, and the slave 155 | sends less than 64 payload symbols to the master, then the master will wait a 156 | random number of bit times (up to 64) before sending the next packet to the 157 | slave. 158 | 159 | 160 | Low-level signaling 161 | =================== 162 | 163 | The ponylink protocol does not need to recover a bit clock on the recieving end. 164 | Instead of transmitting individual bits, the sender looks at sequences of identical 165 | bits (i.e. 0-pulses and 1-pulses). The 8b10b encoding guarantees that there are 166 | never more than 5 identical bits in a sequence. 167 | 168 | The tool "timings.py" first determines the shortest pulse that the sender can 169 | produce that is guaranteed to be detected by the reciever. I.e. the shortest 170 | pulse the sender can produce so that the reciever samples the pulse at least 171 | once. Lets call this pulse a type-1 pulse. 172 | 173 | Next it determines the shortes pulse that the sender can produce that is 174 | guaranteed to be sampled more often by the reciever that a type-1 pulse can 175 | every be sampled by the reciever. This is a type-2 pulse, and so on. 176 | 177 | The sender encodes a 1 bit wide pulse of 8b10b data as type-1 pulse, a 2 bit 178 | wide pulse as type-2 pulse, and so on. 179 | 180 | The reciever then samples the data wire, determines the length of each pulse in 181 | number of samples, and converts that back to a pulse of the right length in the 182 | 8b10b data stream. 183 | 184 | This scheme works best if different clock frequencies are used for the master 185 | and slave core. It is recommended to play with different parameter values for 186 | timings.py to explore this space. 187 | 188 | For example, the following is the configuration used for a master running at a 189 | 250 MHz clock and using a 4-bit SERDES (1 GS/s = 1ns), a slave with a 100 MHz 190 | clock using a 2 bit SERDES (200 MS/s = 5ns) and a maximal jitter on 0.5 ns: 191 | 192 | $ python timings.py 1 5 0.5 0.5 193 | 194 | ** TIMING SPECIFICATION SUMMARY ** 195 | Master clock: 1.000 ns (1000.00 MHz) 196 | Slave clock: 5.000 ns (200.00 MHz) 197 | Master->Slave pulse jitter: 0.500 ns 198 | Slave->Master pulse jitter: 0.500 ns 199 | 200 | ** FINDING TIMING CONFIG FOR DIRECTION 'MASTER -> SLAVE' ** 201 | Timing for transmit_period=1.000 ns (1000.00 MHz) and sample_period=5.000 ns (200.00 MHz) 202 | transmit timings: [6, 16, 26, 36, 46] 203 | 1 - 2 identical samples -> 1 bit 204 | 3 - 4 identical samples -> 2 bits 205 | 5 - 6 identical samples -> 3 bits 206 | 7 - 8 identical samples -> 4 bits 207 | 9 - 10 identical samples -> 5 bits 208 | Bitrate vs. pulse length: 209 | @1: 166.67 MBit/s (expected 32%) 210 | @2: 125.00 MBit/s (expected 36%) 211 | @3: 115.38 MBit/s (expected 21%) 212 | @4: 111.11 MBit/s (expected 7%) 213 | @5: 108.70 MBit/s (expected 1%) 214 | ==> 135.27 MBit/s (expected avg.) 215 | TRANSMIT TIMINGS: [6, 16, 26, 36, 46] 216 | SAMPLE TIMINGS: [1, 3, 5, 7, 9] 217 | 218 | ** FINDING TIMING CONFIG FOR DIRECTION 'SLAVE -> MASTER' ** 219 | Timing for transmit_period=5.000 ns (200.00 MHz) and sample_period=1.000 ns (1000.00 MHz) 220 | transmit timings: [1, 2, 3, 4, 5] 221 | 4 - 6 identical samples -> 1 bit 222 | 9 - 11 identical samples -> 2 bits 223 | 14 - 16 identical samples -> 3 bits 224 | 19 - 21 identical samples -> 4 bits 225 | 24 - 26 identical samples -> 5 bits 226 | Bitrate vs. pulse length: 227 | @1: 200.00 MBit/s (expected 32%) 228 | @2: 200.00 MBit/s (expected 36%) 229 | @3: 200.00 MBit/s (expected 21%) 230 | @4: 200.00 MBit/s (expected 7%) 231 | @5: 200.00 MBit/s (expected 1%) 232 | ==> 200.00 MBit/s (expected avg.) 233 | TRANSMIT TIMINGS: [1, 2, 3, 4, 5] 234 | SAMPLE TIMINGS: [4, 9, 14, 19, 24] 235 | 236 | ** CORE CONFIGURATION ** 237 | .MASTER_TIMINGS(80'h2e241a100618130e0904), 238 | .SLAVE_TIMINGS(80'h05040302010907050301) 239 | 240 | Lets look for example at a 3-bit wide pulse in the 8b10b data sent from the 241 | master to the slave. As can be seen in the "transmit timings" line produced for 242 | the master->slave direction, this would be encoded as a 26 (master) samples 243 | wide pulse on the data line. On the slave this would be received as a pulse 244 | that is 5 or 6 samples wide, which can be uniquely identified as a pulse that 245 | is 3 data bits wide. 246 | 247 | Notice that in this case the slave samples with 200.00 MHz, but can receive 248 | data at 135.27 MBit/s. Therefore this encoding scheme requires the slave to do 249 | less than 2x oversampling in this case. 250 | 251 | -------------------------------------------------------------------------------- /plinksrc/synth_icecube.sdc: -------------------------------------------------------------------------------- 1 | # over-constraint to 200 MHz -> Design meets 100 MHz target 2 | create_clock -period 5.00 -name clk [get_ports clk] 3 | -------------------------------------------------------------------------------- /plinksrc/synth_vivado.tcl: -------------------------------------------------------------------------------- 1 | 2 | # vivado -nojournal -log synth_vivado.log -mode batch -source synth_vivado.tcl 3 | 4 | read_verilog ponylink_master.v 5 | read_verilog ponylink_slave.v 6 | read_verilog ponylink_pack.v 7 | read_verilog ponylink_txrx.v 8 | read_verilog ponylink_8b10b.v 9 | read_verilog ponylink_crc32.v 10 | read_xdc synth_vivado.xdc 11 | 12 | synth_design -part xc7z010clg400-2 -top ponylink_master 13 | opt_design 14 | place_design 15 | route_design 16 | 17 | report_utilization 18 | report_timing 19 | 20 | write_verilog -force synth_vivado.v 21 | 22 | -------------------------------------------------------------------------------- /plinksrc/synth_vivado.xdc: -------------------------------------------------------------------------------- 1 | create_clock -period 4.00 [get_ports clk] 2 | -------------------------------------------------------------------------------- /plinksrc/synth_yosys.ys: -------------------------------------------------------------------------------- 1 | 2 | # yosys -l synth_yosys.log synth_yosys.ys 3 | 4 | read_verilog ponylink_master.v 5 | read_verilog ponylink_slave.v 6 | read_verilog ponylink_pack.v 7 | read_verilog ponylink_txrx.v 8 | read_verilog ponylink_8b10b.v 9 | read_verilog ponylink_crc32.v 10 | 11 | synth_xilinx -top ponylink_master 12 | 13 | -------------------------------------------------------------------------------- /plinksrc/timings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # timings.py is a script to calculate transmitter timing configurations between 4 | # a master and a slave core, given their timing parameters. 5 | # 6 | # Example usage: 7 | # python timings.py 6 19 0.25 1.5 8 | # | | | | 9 | # Master clock period | | Slave to Master pulse jitter 10 | # | | 11 | # Slave clock period Master to slave pulse jitter 12 | # 13 | # All parameters are given in nanoseconds. 14 | # 15 | # Please refer to the "protocol.txt" file for a more detailed explanation of 16 | # how to use this tool. 17 | 18 | from __future__ import division 19 | from __future__ import print_function 20 | 21 | import sys 22 | import numpy as np 23 | import matplotlib.pyplot as plt 24 | 25 | class TimingSolver: 26 | """ 27 | Solves the constraints required for an asynchronously clocked receiver 28 | to unambiguously decode an asynchronously clocked transmitter's bit stream. 29 | 30 | What follows is a bit of theory behind why this class is so useful. 31 | 32 | In a synchronous circuit, we could arrange the transmitter and receiver to 33 | use the same clock. This is what SPI does, for instance, and it eliminates 34 | the need for clock recovery in most cases. It also offers the best 35 | possible performance. However, many applications can get by with "fast 36 | enough" communications, and so do not need the additional overhead of a 37 | dedicated clock. Clocks are particularly unwanted when using differential 38 | signalling, since they take up valuable I/O pins, already rapidly exhausted 39 | thanks to needing two per signal. 40 | 41 | With asynchronous transmission, however, the receiver and transmitter need 42 | not share a single clock. Their clocks just need to be "close enough." 43 | This is how RS-232 communications work, for instance. However, the 44 | disadvantage here is that your endpoints must somehow agree ahead of time 45 | on the transmission rate, and their sampling clocks must fall within some 46 | small percentage of each other for the link to communicate at all. 47 | 48 | PonyLink is an asynchronous transmission technology which allows the 49 | receiver and transmitter to have wildly different clocks. It does this by 50 | allowing the transmitter to send pulses long enough to guarantee the 51 | receiver at least one opportunity to recognize different transmitted bit 52 | patterns. The transmitter must know, a priori, some timing details about 53 | the receiver for this to happen. Knowing what information to encode for 54 | this to happen is the reason for this class. 55 | 56 | Let's suppose we want to transmit the bits 01000001. We can break this 57 | down into four pieces: a 0-vector of length 1, a 1-vector of length 1, a 58 | 0-vector of length 5, and a 1-vector of length 1. Notice how we always 59 | alternate between 0s and 1s. If we plot this on a timing diagram, we 60 | might see a pattern like: 61 | 62 | ___ ___ 63 | \___/ \_________________________/ \___ 64 | | | | | | | | | -- RX sample points 65 | 0 1 0 0 0 0 0 1 66 | 67 | The vertical bars indicate sampling points needed by the receiver or 68 | transmitter. But, if the transmitter is 2x faster than the receiver, it 69 | follows that the transmitter will need to send twice as many bits as the 70 | receiver will sample to see the desired bit pattern: 71 | 72 | ___ ___ 73 | \___/ \_________________________/ \___ 74 | | | | | | | | | -- RX sample points 75 | 0 1 0 0 0 0 0 1 76 | 77 | | | | | | | | | | | | | | | | | -- TX sample points 78 | 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 79 | 80 | As long as the ratio of transmitter to receiver clocking is a convenient 81 | power of two, you can get by with making circuits yourself, by hand, that 82 | meet these requirements. But when you have circuits with inconvenient 83 | clocking ratios, what happens then? You end up needing to send pulses with 84 | slightly different durations to represent the same actual pulse stream on 85 | the receiver; indeed, PonyLink's key innovation is the recognition of 86 | bounded ranges of pulse trains representing symbols. 87 | 88 | For example, you might have noticed the not-quite-even spacing used in the 89 | TX sample points row in the above diagram. That's because my math was 90 | slightly off; I was going to fix it, but it actually illustrates the value 91 | of this class perfectly. Let's redraw the graph, as-is, but with a more 92 | realistic TX sample spacing: 93 | ___ ___ 94 | \___/ \_________________________/ \___ 95 | | | | | | | | | -- RX sample points 96 | 0 1 0 0 0 0 0 1 97 | 98 | | | | | | | | | | | | | | | | | | | | -- TX sample points 99 | 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 100 | 101 | From this diagram, can you tell how many zeros are minimally or maximally 102 | needed to decode a stream of five contiguous data 0 bits? Depending on 103 | phase relationships, exact timing of the transmitter, and that of the 104 | receiver, it can vary between 10 and 14 zeros. In the above example, I 105 | draw 13 0s. 106 | 107 | Reliably computing these bounded ranges is the purpose of this class. 108 | """ 109 | 110 | def __init__(self): 111 | # the values for self.lendist come from bitdist.py 112 | self.lendist = [ 0.32735297, 0.36519647, 0.21898804, 0.07264801, 0.0158145 ] 113 | self.results = { } 114 | 115 | def test_recv_timings(self, transmit_period, sample_period, pulse_jitter, transmit_timings, verbose): 116 | """ 117 | Answers with an array of receiver sample timings. Each timing 118 | parameter determines the number of identical samples the receiver must 119 | receive to correspond to a bit vector of a given length. The 0th 120 | element determines the number of samples for a single bit, 1st element 121 | corresponds to the number of samples for two bits, etc. 122 | 123 | The transmit_period determines the transmitter's NOMINAL serializer 124 | clock rate. For example, if your transmitter operates at 100MHz, this 125 | value is 10. It's assumed to be in nanoseconds. The sample_period 126 | determines the receiver's NOMINAL sampling rate, in exactly the same 127 | way. It, too, is assumed to be nanoseconds. 128 | 129 | pulse_jitter (also in nanoseconds) specifies worst-case boundaries for 130 | timing. For example, if given a value of 0.5 and a transmit_period of 131 | 10, then we expect the transmitter to send with a period between 9.5 132 | and 10.5ns, with an AVERAGE period of 10ns. 133 | 134 | The transmit_timings parameter is a list of numbers. The 0th element 135 | determines the number transmit_periods required for the receiver to 136 | identify a single bit's worth of encoded data. The 1st element 137 | determines the number of transmit_periods required for the receiver to 138 | identify a span of two bits worth of data. Etc. If you can guarantee 139 | your transmitter will never produce a continuous stream of 1s or 0s 140 | longer than N, then the length of this list must be N as well. E.g., 141 | for PonyLink, this len(transmit_timings)=5. 142 | 143 | If verbose is truthy, stdout will receive a diagnostic report of the 144 | samples calculated. 145 | """ 146 | sample_timings = [ ] 147 | last_maxsamples = 0 148 | if verbose: 149 | print("Timing for transmit_period=%.3f ns (%.2f MHz) and sample_period=%.3f ns (%.2f MHz)" % (transmit_period, 1000 / transmit_period, sample_period, 1000 / sample_period)) 150 | print(" transmit timings: %s" % transmit_timings) 151 | for bits in range(1, len(transmit_timings)+1): 152 | minsamples = int((transmit_timings[bits-1] * transmit_period - pulse_jitter) / sample_period) 153 | maxsamples = int((transmit_timings[bits-1] * transmit_period + pulse_jitter) / sample_period + 1) 154 | if verbose: 155 | print(" %2d - %2d identical samples -> %d bit%s" % (minsamples, maxsamples, bits, "s" if bits != 1 else "")) 156 | if minsamples <= last_maxsamples: 157 | if verbose: 158 | print(" collision!") 159 | return [] 160 | last_maxsamples = maxsamples 161 | sample_timings.append(minsamples) 162 | return sample_timings 163 | 164 | def find_config(self, direction, send_period, recv_period, pulse_jitter): 165 | """ 166 | Compute and report the timing configuration needed by a master and/or slave to its peer. 167 | """ 168 | print() 169 | print("** FINDING TIMING CONFIG FOR DIRECTION '%s' **" % ("MASTER -> SLAVE" if direction == 0 else "SLAVE -> MASTER")) 170 | transmit_timings = [] 171 | for bits in range(1, 6): 172 | transmit_timings.append(1 if bits <= 1 else transmit_timings[-1] + 1) 173 | while True: 174 | sample_timings = self.test_recv_timings(send_period, recv_period, pulse_jitter, transmit_timings, False) 175 | if len(sample_timings) > 0: 176 | break 177 | transmit_timings[-1] += 1 178 | self.test_recv_timings(send_period, recv_period, pulse_jitter, transmit_timings, True) 179 | print("Bitrate vs. pulse length:"); 180 | avgbitrate = 0 181 | for bits in range(1, 6): 182 | print(" @%d: %6.2f MBit/s (expected %2d%%)" % (bits, (1000 / send_period) * bits / transmit_timings[bits-1], 100*self.lendist[bits-1])) 183 | avgbitrate += self.lendist[bits-1] * (1000 / send_period) * bits / transmit_timings[bits-1] 184 | print(" ==> %6.2f MBit/s (expected avg.)" % avgbitrate) 185 | self.results["M2S_BW" if direction == 0 else "S2M_BW"] = avgbitrate 186 | self.results["M2S_TT" if direction == 0 else "S2M_TT"] = transmit_timings 187 | self.results["M2S_ST" if direction == 0 else "S2M_ST"] = sample_timings 188 | print("TRANSMIT TIMINGS: %s" % transmit_timings); 189 | print("SAMPLE TIMINGS: %s" % sample_timings); 190 | 191 | if __name__ == "__main__": 192 | if len(sys.argv) == 5: 193 | master_period = float(sys.argv[1]) 194 | slave_period = float(sys.argv[2]) 195 | m2s_pulse_jitter = float(sys.argv[3]) 196 | s2m_pulse_jitter = float(sys.argv[4]) 197 | 198 | print() 199 | print("** TIMING SPECIFICATION SUMMARY **") 200 | print(" Master clock: %.3f ns (%.2f MHz)" % (master_period, 1000 / master_period)) 201 | print(" Slave clock: %.3f ns (%.2f MHz)" % (slave_period, 1000 / slave_period)) 202 | print(" Master->Slave pulse jitter: %.3f ns" % (m2s_pulse_jitter)) 203 | print(" Slave->Master pulse jitter: %.3f ns" % (s2m_pulse_jitter)) 204 | 205 | solver = TimingSolver() 206 | solver.find_config(0, master_period, slave_period, m2s_pulse_jitter); 207 | solver.find_config(1, slave_period, master_period, s2m_pulse_jitter); 208 | 209 | print() 210 | print("** CORE CONFIGURATION **") 211 | print(".MASTER_TIMINGS(80'h%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x)," % ( 212 | solver.results["M2S_TT"][4], solver.results["M2S_TT"][3], solver.results["M2S_TT"][2], solver.results["M2S_TT"][1], solver.results["M2S_TT"][0], 213 | solver.results["S2M_ST"][4], solver.results["S2M_ST"][3], solver.results["S2M_ST"][2], solver.results["S2M_ST"][1], solver.results["S2M_ST"][0])) 214 | print(".SLAVE_TIMINGS(80'h%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x)" % ( 215 | solver.results["S2M_TT"][4], solver.results["S2M_TT"][3], solver.results["S2M_TT"][2], solver.results["S2M_TT"][1], solver.results["S2M_TT"][0], 216 | solver.results["M2S_ST"][4], solver.results["M2S_ST"][3], solver.results["M2S_ST"][2], solver.results["M2S_ST"][1], solver.results["M2S_ST"][0])) 217 | print() 218 | 219 | elif len(sys.argv) == 2 and sys.argv[1] == "-plot": 220 | x_slave_period = list() 221 | y_m2s_bitrate = list() 222 | y_s2m_bitrate = list() 223 | 224 | for k in range(50, 200): 225 | master_period = 100.0 226 | slave_period = k 227 | m2s_pulse_jitter = 0.5 228 | s2m_pulse_jitter = 0.5 229 | 230 | solver = TimingSolver() 231 | solver.find_config(0, master_period, slave_period, m2s_pulse_jitter); 232 | solver.find_config(1, slave_period, master_period, s2m_pulse_jitter); 233 | 234 | x_slave_period.append(slave_period) 235 | y_m2s_bitrate.append(solver.results["M2S_BW"]) 236 | y_s2m_bitrate.append(solver.results["S2M_BW"]) 237 | 238 | from matplotlib import pyplot as plt 239 | 240 | x_slave_period = np.array(x_slave_period) 241 | y_m2s_bitrate = np.array(y_m2s_bitrate) 242 | y_s2m_bitrate = np.array(y_s2m_bitrate) 243 | 244 | plt.figure(figsize=(10, 5)) 245 | plt.title("Bandwidth, normalized using master clock frequency") 246 | plt.plot(x_slave_period / 100, y_m2s_bitrate / 10, label="master -> slave") 247 | plt.plot(x_slave_period / 100, y_s2m_bitrate / 10, label="slave -> master") 248 | plt.ylabel("Bandwidth (MBit/s / MHz master clock)") 249 | plt.xlabel("Ratio of slave clock period to master clock period " + 250 | "(<1.0 = slave clock is faster than master clock)") 251 | plt.semilogx() 252 | xticks = [0.5, 0.7, 1.0, 1.5, 2.0] 253 | plt.xticks(xticks, xticks) 254 | plt.xlim(0.5, 2.0) 255 | plt.ylim(0, 1.2) 256 | plt.legend() 257 | plt.show() 258 | 259 | else: 260 | sys.exit( 261 | ('Usage: %s \\\n' % sys.argv[0]) + 262 | (' ')) 263 | 264 | -------------------------------------------------------------------------------- /smt2bmc/README: -------------------------------------------------------------------------------- 1 | 2 | Formal proofs using SMT2 solvers (Yices, Z3, CVC4, MathSAT, etc.) 3 | 4 | 5 | chip 6 | ---- 7 | 8 | Post-synthesis proofs from lattice ice40 bitstream files. Make changes as 9 | neccessary to perform a proof. (Used only for hardcore debugging sessions.) 10 | 11 | 12 | slave_send_length 13 | ----------------- 14 | 15 | Proves that when the slave is sending, it will ultimately stop sending after at 16 | least 800 cycles (for the most simple [1 2 3 4 5] send timings). 17 | 18 | -------------------------------------------------------------------------------- /smt2bmc/chip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys, getopt 4 | from smtio import * 5 | 6 | steps = 1000 7 | so = smtopts() 8 | 9 | def usage(): 10 | print(""" 11 | python3 slave_send_length.py [options] 12 | 13 | -t 14 | default: 1000 15 | """ + so.helpmsg()) 16 | sys.exit(1) 17 | 18 | try: 19 | opts, args = getopt.getopt(sys.argv[1:], "s:t:vd") 20 | except: 21 | usage() 22 | 23 | for o, a in opts: 24 | if o == "-t": 25 | steps = int(a) 26 | elif so.handle(o, a): 27 | pass 28 | else: 29 | usage() 30 | 31 | if len(args) > 0: 32 | usage() 33 | 34 | smt = smtio(opts=so) 35 | 36 | print("Solver: %s" % so.solver) 37 | smt.setup("QF_AUFBV", "PonyLink 'chip.py' test, Powered by Yosys") 38 | 39 | with open("chip.smt2", "r") as f: 40 | for line in f: 41 | smt.write(line) 42 | 43 | found_max_range = False 44 | 45 | for step in range(steps): 46 | print("%s Searching for send sequence of length %d.." % (smt.timestamp(), step+1)) 47 | smt.write("(declare-fun s%d () chip_s)" % step) 48 | smt.write("(assert (|chip_n pin_F16| s%d))" % step) 49 | 50 | if step != 0: 51 | smt.write("(assert (chip_t s%d s%d))" % (step-1, step)) 52 | 53 | if smt.check_sat() == "unsat": 54 | found_max_range = True 55 | print("%s Maximum send length reached.\n") 56 | break 57 | 58 | print("%s Done." % smt.timestamp()) 59 | smt.write("(exit)") 60 | smt.wait() 61 | 62 | -------------------------------------------------------------------------------- /smt2bmc/chip.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | # iceunpack chip.bin chip.txt 4 | # icebox_vlog -ls chip.txt > chip.v 5 | yosys -v3 chip.ys 6 | python3 -u chip.py -d chip.dbg | tee chip.log 7 | -------------------------------------------------------------------------------- /smt2bmc/chip.ys: -------------------------------------------------------------------------------- 1 | 2 | read_verilog chip.v 3 | read_verilog -defer +/ice40/cells_sim.v 4 | 5 | hierarchy -top chip 6 | proc; opt; flatten; opt 7 | memory -nomap -nordff; opt; 8 | techmap; opt -fast; abc; opt -fast 9 | write_smt2 -bv -mem chip.smt2 10 | 11 | -------------------------------------------------------------------------------- /smt2bmc/reset_sequence.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys, getopt, re 4 | from smtio import * 5 | from time import time 6 | from shutil import copyfile 7 | 8 | so = smtopts() 9 | 10 | 11 | def usage(): 12 | print(""" 13 | python3 reset_sequence.py [options] 14 | """ + so.helpmsg()) 15 | sys.exit(1) 16 | 17 | 18 | try: 19 | opts, args = getopt.getopt(sys.argv[1:], so.optstr) 20 | except: 21 | usage() 22 | 23 | for o, a in opts: 24 | if so.handle(o, a): 25 | pass 26 | else: 27 | usage() 28 | 29 | if len(args) > 0: 30 | usage() 31 | 32 | 33 | print("Solver: %s" % so.solver) 34 | 35 | smt = smtio(opts=so) 36 | smt.setup("QF_AUFBV", "PonyLink 'reset_sequence.py' test, Powered by Yosys") 37 | 38 | debug_nets = set() 39 | debug_nets_re = re.compile(r"^; yosys-smt2-(input|output|register|wire) (\S+) (\d+)") 40 | with open("reset_sequence.smt2", "r") as f: 41 | for line in f: 42 | match = debug_nets_re.match(line) 43 | if match: 44 | debug_nets.add(match.group(2)) 45 | smt.write(line) 46 | 47 | 48 | last_dump_time = time() 49 | last_dump_step = -90 50 | mode_min = True 51 | step = -1 52 | 53 | 54 | def add_step(): 55 | global step 56 | step += 1 57 | 58 | print("%s Adding time step %d." % (smt.timestamp(), step)) 59 | smt.write("(declare-fun s%d () main_s)" % step) 60 | 61 | if step == 0: 62 | smt.write("(assert (|main_n reset| s%d))" % step) 63 | 64 | else: 65 | smt.write("(assert (not (|main_n reset| s%d)))" % step) 66 | smt.write("(assert (main_t s%d s%d))" % (step-1, step)) 67 | 68 | 69 | def write_vcd_model(): 70 | print("%s Writing model to VCD file." % smt.timestamp()) 71 | 72 | vcd = mkvcd(open("reset_sequence.vcd", "w")) 73 | for netname in sorted(debug_nets): 74 | width = len(smt.get_net_bin("main", netname, "s0")) 75 | vcd.add_net(netname, width) 76 | 77 | for i in range(step): 78 | vcd.set_time(i) 79 | for netname in debug_nets: 80 | vcd.set_net(netname, smt.get_net_bin("main", netname, "s%d" % i)) 81 | 82 | 83 | print("%s PHASE 1: Minimal length search" % smt.timestamp()) 84 | 85 | add_step() 86 | 87 | while True: 88 | for i in range(10): 89 | add_step() 90 | 91 | print("%s Searching for minimal length reset sequence.." % smt.timestamp()) 92 | 93 | smt.write("(push 1)") 94 | smt.write("(assert (|main_n out_finish| s%d))" % step) 95 | 96 | if smt.check_sat() == "unsat": 97 | print("%s [unsat] no such reset sequence." % smt.timestamp()) 98 | smt.write("(pop 1)") 99 | 100 | if step % 50 == 0: 101 | print("%s Creating unconstrained model.." % smt.timestamp()) 102 | assert smt.check_sat() == "sat" 103 | write_vcd_model() 104 | 105 | else: 106 | print("%s [sat] reset sequence found." % smt.timestamp()) 107 | write_vcd_model() 108 | copyfile("reset_sequence.vcd", "reset_sequence_min.vcd") 109 | smt.write("(pop 1)") 110 | break 111 | 112 | 113 | print("%s PHASE 2: Maximal length search" % smt.timestamp()) 114 | 115 | while True: 116 | for i in range(10): 117 | add_step() 118 | 119 | print("%s Searching for maximal length reset sequence.." % smt.timestamp()) 120 | 121 | smt.write("(push 1)") 122 | smt.write("(assert (not (|main_n out_finish| s%d)))" % step) 123 | 124 | if smt.check_sat() == "unsat": 125 | print("%s [unsat] maximal length reached." % smt.timestamp()) 126 | smt.write("(pop 1)") 127 | break 128 | 129 | else: 130 | print("%s [sat] reset sequence found." % smt.timestamp()) 131 | write_vcd_model() 132 | 133 | 134 | print("%s PHASE 3: Maximal length refinement" % smt.timestamp()) 135 | 136 | for i in range(step-10, step+1): 137 | print("%s Testing length %d.." % (smt.timestamp(), i)) 138 | 139 | smt.write("(push 1)") 140 | smt.write("(assert (not (|main_n out_finish| s%d)))" % i) 141 | 142 | if smt.check_sat() == "unsat": 143 | print("%s [unsat] maximal length reached." % smt.timestamp()) 144 | smt.write("(pop 1)") 145 | copyfile("reset_sequence.vcd", "reset_sequence_max.vcd") 146 | break 147 | 148 | else: 149 | print("%s [sat] reset sequence found." % smt.timestamp()) 150 | write_vcd_model() 151 | 152 | 153 | print("%s Done." % smt.timestamp()) 154 | smt.write("(exit)") 155 | smt.wait() 156 | 157 | -------------------------------------------------------------------------------- /smt2bmc/reset_sequence.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | yosys -v3 reset_sequence.ys 4 | python3 -u reset_sequence.py -d reset_sequence.dbg | tee reset_sequence.log 5 | -------------------------------------------------------------------------------- /smt2bmc/reset_sequence.v: -------------------------------------------------------------------------------- 1 | module main ( 2 | input slave_clk, 3 | input master_clk, 4 | input reset, 5 | output out_finish, 6 | output out_error 7 | ); 8 | wire slave_clk; 9 | wire slave_resetn_out; 10 | wire slave_linkerror; 11 | wire slave_linkready; 12 | wire slave_mode_recv; 13 | wire slave_mode_send; 14 | wire [7:0] slave_gpio_i = 0; 15 | wire [7:0] slave_gpio_o; 16 | wire [7:0] slave_in_tdata; 17 | wire [3:0] slave_in_tuser; 18 | wire slave_in_tvalid; 19 | wire slave_in_tlast; 20 | wire slave_in_tready; 21 | wire [7:0] slave_out_tdata; 22 | wire [3:0] slave_out_tuser; 23 | wire slave_out_tvalid; 24 | wire slave_out_tlast; 25 | wire slave_out_tready; 26 | wire [3:0] slave_serdes_in; 27 | wire [3:0] slave_serdes_out; 28 | wire [3:0] slave_serdes_en; 29 | 30 | assign slave_in_tdata = slave_out_tdata; 31 | assign slave_in_tuser = slave_out_tuser; 32 | assign slave_in_tvalid = slave_out_tvalid; 33 | assign slave_in_tlast = slave_out_tlast; 34 | assign slave_out_tready = slave_in_tready; 35 | 36 | wire master_clk; 37 | wire master_resetn; 38 | wire master_linkerror; 39 | wire master_linkready; 40 | wire master_mode_recv; 41 | wire master_mode_send; 42 | wire [7:0] master_gpio_i = 0; 43 | wire [7:0] master_gpio_o; 44 | wire [7:0] master_in_tdata = 23; 45 | wire [3:0] master_in_tuser = 13; 46 | wire master_in_tvalid; 47 | wire master_in_tlast = 0; 48 | wire master_in_tready; 49 | wire [7:0] master_out_tdata; 50 | wire [3:0] master_out_tuser; 51 | wire master_out_tvalid; 52 | wire master_out_tlast; 53 | wire master_out_tready = 0; 54 | wire [3:0] master_serdes_in; 55 | wire [3:0] master_serdes_out; 56 | wire [3:0] master_serdes_en; 57 | 58 | assign master_serdes_in = slave_serdes_in, slave_serdes_in = 59 | |master_serdes_en && !slave_serdes_en ? master_serdes_out : 60 | !master_serdes_en && |slave_serdes_en ? slave_serdes_out : 0; 61 | 62 | assign out_finish = !reset && master_resetn && master_out_tvalid; 63 | assign out_error = (master_out_tdata != 23 || master_out_tuser != 13); 64 | 65 | reg master_resetn, master_in_tvalid; 66 | reg [1:0] state; 67 | 68 | always @(posedge master_clk) begin 69 | if (reset) begin 70 | master_resetn <= 0; 71 | master_in_tvalid <= 1; 72 | state <= 0; 73 | end else 74 | case (state) 75 | 0: begin 76 | master_resetn <= 1; 77 | state <= 1; 78 | end 79 | 1: begin 80 | if (master_in_tready) begin 81 | master_in_tvalid <= 0; 82 | state <= 2; 83 | end 84 | end 85 | endcase 86 | end 87 | 88 | ponylink_slave #( 89 | .SERDES_REG_IN(1), 90 | .SERDES_REG_OUT(1), 91 | .M2S_TDATA_WIDTH(8), 92 | .M2S_TUSER_WIDTH(4), 93 | .S2M_TDATA_WIDTH(8), 94 | .S2M_TUSER_WIDTH(4), 95 | .MASTER_RECV_DELAY(8), 96 | .SLAVE_RECV_DELAY(8), 97 | .MASTER_SEND_DELAY(32), 98 | .SLAVE_SEND_DELAY(32), 99 | .MASTER_PARBITS(4), 100 | .SLAVE_PARBITS(4), 101 | .MASTER_PKTLEN(8), 102 | .SLAVE_PKTLEN(8), 103 | .MASTER_TIMINGS(80'h0e0b0805020d0a070401), 104 | .SLAVE_TIMINGS(80'h0e0b0805020d0a070401) 105 | ) uut_slave ( 106 | .clk (slave_clk ), 107 | .resetn_out(slave_resetn_out), 108 | .linkerror (slave_linkerror ), 109 | .linkready (slave_linkready ), 110 | .mode_recv (slave_mode_recv ), 111 | .mode_send (slave_mode_send ), 112 | .gpio_i (slave_gpio_i ), 113 | .gpio_o (slave_gpio_o ), 114 | .in_tdata (slave_in_tdata ), 115 | .in_tuser (slave_in_tuser ), 116 | .in_tvalid (slave_in_tvalid ), 117 | .in_tlast (slave_in_tlast ), 118 | .in_tready (slave_in_tready ), 119 | .out_tdata (slave_out_tdata ), 120 | .out_tuser (slave_out_tuser ), 121 | .out_tvalid(slave_out_tvalid), 122 | .out_tlast (slave_out_tlast ), 123 | .out_tready(slave_out_tready), 124 | .serdes_in (slave_serdes_in ), 125 | .serdes_out(slave_serdes_out), 126 | .serdes_en (slave_serdes_en ) 127 | ); 128 | 129 | ponylink_master #( 130 | .SERDES_REG_IN(1), 131 | .SERDES_REG_OUT(1), 132 | .M2S_TDATA_WIDTH(8), 133 | .M2S_TUSER_WIDTH(4), 134 | .S2M_TDATA_WIDTH(8), 135 | .S2M_TUSER_WIDTH(4), 136 | .MASTER_RECV_DELAY(8), 137 | .SLAVE_RECV_DELAY(8), 138 | .MASTER_SEND_DELAY(32), 139 | .SLAVE_SEND_DELAY(32), 140 | .MASTER_PARBITS(4), 141 | .SLAVE_PARBITS(4), 142 | .MASTER_PKTLEN(8), 143 | .SLAVE_PKTLEN(8), 144 | .MASTER_TIMINGS(80'h0e0b0805020d0a070401), 145 | .SLAVE_TIMINGS(80'h0e0b0805020d0a070401) 146 | ) uut_master ( 147 | .clk (master_clk ), 148 | .resetn (master_resetn ), 149 | .linkerror (master_linkerror ), 150 | .linkready (master_linkready ), 151 | .mode_recv (master_mode_recv ), 152 | .mode_send (master_mode_send ), 153 | .gpio_i (master_gpio_i ), 154 | .gpio_o (master_gpio_o ), 155 | .in_tdata (master_in_tdata ), 156 | .in_tuser (master_in_tuser ), 157 | .in_tvalid (master_in_tvalid ), 158 | .in_tlast (master_in_tlast ), 159 | .in_tready (master_in_tready ), 160 | .out_tdata (master_out_tdata ), 161 | .out_tuser (master_out_tuser ), 162 | .out_tvalid(master_out_tvalid), 163 | .out_tlast (master_out_tlast ), 164 | .out_tready(master_out_tready), 165 | .serdes_in (master_serdes_in ), 166 | .serdes_out(master_serdes_out), 167 | .serdes_en (master_serdes_en ) 168 | ); 169 | endmodule 170 | -------------------------------------------------------------------------------- /smt2bmc/reset_sequence.ys: -------------------------------------------------------------------------------- 1 | 2 | read_verilog ../plinksrc/ponylink_slave.v 3 | read_verilog ../plinksrc/ponylink_master.v 4 | read_verilog ../plinksrc/ponylink_pack.v 5 | read_verilog ../plinksrc/ponylink_txrx.v 6 | read_verilog ../plinksrc/ponylink_8b10b.v 7 | read_verilog ../plinksrc/ponylink_crc32.v 8 | 9 | read_verilog -formal reset_sequence.v 10 | 11 | hierarchy -check -top main 12 | setattr -set keep 1 main/w:\* 13 | 14 | proc; opt; flatten; opt 15 | memory -nomap -nordff; opt 16 | # techmap; opt -fast; abc; opt -fast 17 | write_smt2 -bv -mem -regs reset_sequence.smt2 18 | 19 | -------------------------------------------------------------------------------- /smt2bmc/slave_send_length.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys, getopt, re 4 | from smtio import * 5 | from time import time 6 | 7 | steps = 650 8 | so = smtopts() 9 | 10 | def usage(): 11 | print(""" 12 | python3 slave_send_length.py [options] 13 | 14 | -t 15 | default: 650 16 | """ + so.helpmsg()) 17 | sys.exit(1) 18 | 19 | try: 20 | opts, args = getopt.getopt(sys.argv[1:], so.optstr + "t:") 21 | except: 22 | usage() 23 | 24 | for o, a in opts: 25 | if o == "-t": 26 | steps = int(a) 27 | elif so.handle(o, a): 28 | pass 29 | else: 30 | usage() 31 | 32 | if len(args) > 0: 33 | usage() 34 | 35 | smt = smtio(opts=so) 36 | 37 | print("Solver: %s" % so.solver) 38 | smt.setup("QF_AUFBV", "PonyLink 'slave_send_length.py' test, Powered by Yosys") 39 | 40 | debug_nets = set() 41 | debug_nets_re = re.compile(r"^; yosys-smt2-(input|output|register|wire) (\S+) (\d+)") 42 | with open("slave_send_length.smt2", "r") as f: 43 | for line in f: 44 | match = debug_nets_re.match(line) 45 | if match: 46 | debug_nets.add(match.group(2)) 47 | smt.write(line) 48 | 49 | def write_vcd_model(num_steps): 50 | print("%s Writing model to VCD file." % smt.timestamp()) 51 | 52 | vcd = mkvcd(open("slave_send_length.vcd", "w")) 53 | for netname in sorted(debug_nets): 54 | width = len(smt.get_net_bin("main", netname, "s0")) 55 | vcd.add_net(netname, width) 56 | 57 | for step in range(num_steps): 58 | vcd.set_time(step) 59 | for netname in debug_nets: 60 | vcd.set_net(netname, smt.get_net_bin("main", netname, "s%d" % step)) 61 | 62 | mode="TX" 63 | last_dump_time = time() 64 | last_dump_step = -90 65 | 66 | for step in range(steps): 67 | print("%s Searching for %s sequence of length %d.." % (smt.timestamp(), mode, step+1)) 68 | smt.write("(declare-fun s%d () main_s)" % step) 69 | 70 | if step != 0: 71 | smt.write("(assert (main_t s%d s%d))" % (step-1, step)) 72 | 73 | smt.write("(push 1)") 74 | smt.write("(assert (|main_n serdes_en| s%d))" % step) 75 | 76 | if smt.check_sat() == "unsat": 77 | if mode == "TX": 78 | print("%s Maximum TX length reached." % smt.timestamp()) 79 | else: 80 | print("%s No TX possible in this cycle." % smt.timestamp()) 81 | 82 | smt.write("(pop 1)") 83 | assert smt.check_sat() == "sat" 84 | mode="extended TX/RX" 85 | 86 | if last_dump_time + 10 < time() or last_dump_step + 100 < step: 87 | write_vcd_model(step+1) 88 | last_dump_time = time() 89 | last_dump_step = step 90 | 91 | if last_dump_step != steps-1: 92 | write_vcd_model(steps) 93 | 94 | print("%s Done." % smt.timestamp()) 95 | smt.write("(exit)") 96 | smt.wait() 97 | 98 | -------------------------------------------------------------------------------- /smt2bmc/slave_send_length.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | yosys -v3 slave_send_length.ys 4 | python3 -u slave_send_length.py -d slave_send_length.dbg | tee slave_send_length.log 5 | -------------------------------------------------------------------------------- /smt2bmc/slave_send_length.v: -------------------------------------------------------------------------------- 1 | module main ( 2 | input clk, 3 | input serdes_in, 4 | output serdes_out, 5 | output serdes_en 6 | ); 7 | wire clk; 8 | wire resetn_out; 9 | wire linkerror; 10 | wire linkready; 11 | wire mode_recv; 12 | wire mode_send; 13 | wire [7:0] gpio_i = 0; 14 | wire [7:0] gpio_o; 15 | wire [4:0] in_tdata = 0; 16 | wire [3:0] in_tuser = 0; 17 | wire in_tvalid = 1; 18 | wire in_tlast = 0; 19 | wire in_tready; 20 | wire [4:0] out_tdata; 21 | wire [3:0] out_tuser; 22 | wire out_tvalid; 23 | wire out_tlast; 24 | wire out_tready = 1; 25 | wire serdes_in; 26 | wire serdes_out; 27 | wire serdes_en; 28 | 29 | ponylink_slave #( 30 | .SERDES_REG_IN(1), 31 | .SERDES_REG_OUT(1), 32 | .M2S_TDATA_WIDTH(8), 33 | .M2S_TUSER_WIDTH(4), 34 | .S2M_TDATA_WIDTH(8), 35 | .S2M_TUSER_WIDTH(4), 36 | .MASTER_RECV_DELAY(8), 37 | .SLAVE_RECV_DELAY(8), 38 | .MASTER_SEND_DELAY(32), 39 | .SLAVE_SEND_DELAY(32), 40 | .MASTER_PARBITS(1), 41 | .SLAVE_PARBITS(1), 42 | .MASTER_PKTLEN(8), 43 | .SLAVE_PKTLEN(8), 44 | .MASTER_TIMINGS(80'h2e241a100618130e0904), 45 | .SLAVE_TIMINGS (80'h05040302010907050301) 46 | ) uut ( 47 | .clk (clk ), 48 | .resetn_out(resetn_out), 49 | .linkerror (linkerror ), 50 | .linkready (linkready ), 51 | .mode_recv (mode_recv ), 52 | .mode_send (mode_send ), 53 | .gpio_i (gpio_i ), 54 | .gpio_o (gpio_o ), 55 | .in_tdata (in_tdata ), 56 | .in_tuser (in_tuser ), 57 | .in_tvalid (in_tvalid ), 58 | .in_tlast (in_tlast ), 59 | .in_tready (in_tready ), 60 | .out_tdata (out_tdata ), 61 | .out_tuser (out_tuser ), 62 | .out_tvalid(out_tvalid), 63 | .out_tlast (out_tlast ), 64 | .out_tready(out_tready), 65 | .serdes_in (serdes_in ), 66 | .serdes_out(serdes_out), 67 | .serdes_en (serdes_en ) 68 | ); 69 | endmodule 70 | -------------------------------------------------------------------------------- /smt2bmc/slave_send_length.ys: -------------------------------------------------------------------------------- 1 | 2 | read_verilog ../plinksrc/ponylink_slave.v 3 | read_verilog ../plinksrc/ponylink_pack.v 4 | read_verilog ../plinksrc/ponylink_txrx.v 5 | read_verilog ../plinksrc/ponylink_8b10b.v 6 | read_verilog ../plinksrc/ponylink_crc32.v 7 | 8 | read_verilog -formal slave_send_length.v 9 | 10 | hierarchy -top main 11 | setattr -set keep 1 main/w:\* 12 | setattr -set keep 1 *ponylink_txrx/w:\* 13 | setattr -set keep 1 *ponylink_txrx_encode*/w:\* 14 | setattr -set keep 1 *ponylink_txrx_decode*/w:\* 15 | 16 | proc; opt; flatten; opt 17 | memory -nomap -nordff; opt 18 | # techmap; opt -fast; abc; opt -fast 19 | write_smt2 -bv -mem -regs slave_send_length.smt2 20 | 21 | -------------------------------------------------------------------------------- /smt2bmc/smtio.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import subprocess 5 | from select import select 6 | from time import time 7 | 8 | class smtio: 9 | def __init__(self, solver=None, debug_print=None, debug_file=None, timeinfo=None, opts=None): 10 | if opts is not None: 11 | self.solver = opts.solver 12 | self.debug_print = opts.debug_print 13 | self.debug_file = opts.debug_file 14 | self.timeinfo = opts.timeinfo 15 | 16 | else: 17 | self.solver = "yices" 18 | self.debug_print = False 19 | self.debug_file = None 20 | self.timeinfo = True 21 | 22 | if solver is not None: 23 | self.solver = solver 24 | 25 | if debug_print is not None: 26 | self.debug_print = debug_print 27 | 28 | if debug_file is not None: 29 | self.debug_file = debug_file 30 | 31 | if timeinfo is not None: 32 | self.timeinfo = timeinfo 33 | 34 | if self.solver == "yices": 35 | popen_vargs = ['yices-smt2', '--incremental'] 36 | 37 | if self.solver == "z3": 38 | popen_vargs = ['z3', '-smt2', '-in'] 39 | 40 | if self.solver == "cvc4": 41 | popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2'] 42 | 43 | if self.solver == "mathsat": 44 | popen_vargs = ['mathsat'] 45 | 46 | self.p = subprocess.Popen(popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 47 | self.start_time = time() 48 | 49 | def setup(self, logic="ALL", info=None): 50 | self.write("(set-logic %s)" % logic) 51 | if info is not None: 52 | self.write("(set-info :source |%s|)" % info) 53 | self.write("(set-info :smt-lib-version 2.5)") 54 | self.write("(set-info :category \"industrial\")") 55 | 56 | def timestamp(self): 57 | secs = int(time() - self.start_time) 58 | return "+ %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60) 59 | 60 | def write(self, stmt): 61 | stmt = stmt.strip() 62 | if self.debug_print: 63 | print("> %s" % stmt) 64 | if self.debug_file: 65 | print(stmt, file=self.debug_file) 66 | self.debug_file.flush() 67 | self.p.stdin.write(bytes(stmt + "\n", "ascii")) 68 | self.p.stdin.flush() 69 | 70 | def read(self): 71 | stmt = [] 72 | count_brackets = 0 73 | 74 | while True: 75 | line = self.p.stdout.readline().decode("ascii").strip() 76 | count_brackets += line.count("(") 77 | count_brackets -= line.count(")") 78 | stmt.append(line) 79 | if self.debug_print: 80 | print("< %s" % line) 81 | if count_brackets == 0: 82 | break 83 | if not self.p.poll(): 84 | print("SMT Solver terminated unexpectedly: %s" % "".join(stmt)) 85 | sys.exit(1) 86 | 87 | stmt = "".join(stmt) 88 | if stmt.startswith("(error"): 89 | print("SMT Solver Error: %s" % stmt, file=sys.stderr) 90 | sys.exit(1) 91 | 92 | return stmt 93 | 94 | def check_sat(self): 95 | if self.debug_print: 96 | print("> (check-sat)") 97 | if self.debug_file: 98 | print("; running check-sat..", file=self.debug_file) 99 | self.debug_file.flush() 100 | 101 | self.p.stdin.write(bytes("(check-sat)\n", "ascii")) 102 | self.p.stdin.flush() 103 | 104 | if self.timeinfo: 105 | i = 0 106 | s = "/-\|" 107 | 108 | count = 0 109 | num_bs = 0 110 | while select([self.p.stdout], [], [], 0.1) == ([], [], []): 111 | count += 1 112 | 113 | if count < 25: 114 | continue 115 | 116 | if count % 10 == 0 or count == 25: 117 | secs = count // 10 118 | 119 | if secs < 60: 120 | m = "(%d seconds)" % secs 121 | elif secs < 60*60: 122 | m = "(%d seconds -- %d:%02d)" % (secs, secs // 60, secs % 60) 123 | else: 124 | m = "(%d seconds -- %d:%02d:%02d)" % (secs, secs // (60*60), (secs // 60) % 60, secs % 60) 125 | 126 | print("%s %s %c" % ("\b \b" * num_bs, m, s[i]), end="", file=sys.stderr) 127 | num_bs = len(m) + 3 128 | 129 | else: 130 | print("\b" + s[i], end="", file=sys.stderr) 131 | 132 | sys.stderr.flush() 133 | i = (i + 1) % len(s) 134 | 135 | if num_bs != 0: 136 | print("\b \b" * num_bs, end="", file=sys.stderr) 137 | sys.stderr.flush() 138 | 139 | result = self.read() 140 | if self.debug_file: 141 | print("(set-info :status %s)" % result, file=self.debug_file) 142 | print("(check-sat)", file=self.debug_file) 143 | self.debug_file.flush() 144 | return result 145 | 146 | def parse(self, stmt): 147 | def worker(stmt): 148 | if stmt[0] == '(': 149 | expr = [] 150 | cursor = 1 151 | while stmt[cursor] != ')': 152 | el, le = worker(stmt[cursor:]) 153 | expr.append(el) 154 | cursor += le 155 | return expr, cursor+1 156 | 157 | if stmt[0] == '|': 158 | expr = "|" 159 | cursor = 1 160 | while stmt[cursor] != '|': 161 | expr += stmt[cursor] 162 | cursor += 1 163 | expr += "|" 164 | return expr, cursor+1 165 | 166 | if stmt[0] in [" ", "\t", "\r", "\n"]: 167 | el, le = worker(stmt[1:]) 168 | return el, le+1 169 | 170 | expr = "" 171 | cursor = 0 172 | while stmt[cursor] not in ["(", ")", "|", " ", "\t", "\r", "\n"]: 173 | expr += stmt[cursor] 174 | cursor += 1 175 | return expr, cursor 176 | return worker(stmt)[0] 177 | 178 | def bv2hex(self, v): 179 | if v == "true": return "1" 180 | if v == "false": return "0" 181 | h = "" 182 | while len(v) > 2: 183 | d = 0 184 | if len(v) > 0 and v[-1] == "1": d += 1 185 | if len(v) > 1 and v[-2] == "1": d += 2 186 | if len(v) > 2 and v[-3] == "1": d += 4 187 | if len(v) > 3 and v[-4] == "1": d += 8 188 | h = hex(d)[2:] + h 189 | if len(v) < 4: break 190 | v = v[:-4] 191 | return h 192 | 193 | def bv2bin(self, v): 194 | if v == "true": return "1" 195 | if v == "false": return "0" 196 | return v[2:] 197 | 198 | def get(self, expr): 199 | self.write("(get-value (%s))" % (expr)) 200 | return self.parse(self.read())[0][1] 201 | 202 | def get_net(self, mod_name, net_name, state_name): 203 | return self.get("(|%s_n %s| %s)" % (mod_name, net_name, state_name)) 204 | 205 | def get_net_bool(self, mod_name, net_name, state_name): 206 | v = self.get_net(mod_name, net_name, state_name) 207 | assert v in ["true", "false"] 208 | return 1 if v == "true" else 0 209 | 210 | def get_net_hex(self, mod_name, net_name, state_name): 211 | return self.bv2hex(self.get_net(mod_name, net_name, state_name)) 212 | 213 | def get_net_bin(self, mod_name, net_name, state_name): 214 | return self.bv2bin(self.get_net(mod_name, net_name, state_name)) 215 | 216 | def wait(self): 217 | self.p.wait() 218 | 219 | 220 | class smtopts: 221 | def __init__(self): 222 | self.optstr = "s:d:vp" 223 | self.solver = "yices" 224 | self.debug_print = False 225 | self.debug_file = None 226 | self.timeinfo = True 227 | 228 | def handle(self, o, a): 229 | if o == "-s": 230 | self.solver = a 231 | elif o == "-v": 232 | self.debug_print = True 233 | elif o == "-p": 234 | self.timeinfo = True 235 | elif o == "-d": 236 | self.debug_file = open(a, "w") 237 | else: 238 | return False 239 | return True 240 | 241 | def helpmsg(self): 242 | return """ 243 | -s 244 | set SMT solver: yices, z3, cvc4, mathsat 245 | default: yices 246 | 247 | -v 248 | enable debug output 249 | 250 | -p 251 | disable timer display during solving 252 | 253 | -d 254 | write smt2 statements to file 255 | """ 256 | 257 | 258 | class mkvcd: 259 | def __init__(self, f): 260 | self.f = f 261 | self.t = -1 262 | self.nets = dict() 263 | 264 | def add_net(self, name, width): 265 | assert self.t == -1 266 | key = "n%d" % len(self.nets) 267 | self.nets[name] = (key, width) 268 | 269 | def set_net(self, name, bits): 270 | assert name in self.nets 271 | assert self.t >= 0 272 | print("b%s %s" % (bits, self.nets[name][0]), file=self.f) 273 | 274 | def set_time(self, t): 275 | assert t >= self.t 276 | if t != self.t: 277 | if self.t == -1: 278 | for name in sorted(self.nets): 279 | key, width = self.nets[name] 280 | print("$var wire %d %s %s $end" % (width, key, name), file=self.f) 281 | print("$enddefinitions $end", file=self.f) 282 | self.t = t 283 | assert self.t >= 0 284 | print("#%d" % self.t, file=self.f) 285 | 286 | -------------------------------------------------------------------------------- /testbench/Makefile: -------------------------------------------------------------------------------- 1 | 2 | N = 100 3 | tests = $(shell gawk 'BEGIN { for (i=0; i < $(N); i++) printf("test_%02d\n", i); exit; }') 4 | 5 | test: $(addsuffix .ok,$(tests)) 6 | @echo 7 | @echo " All tests passed." 8 | @echo 9 | 10 | define test_tpl 11 | $(1).v: maketest.py ../plinksrc/timings.py 12 | python maketest.py > $(1).v 13 | $(1).exe: $(1).v ../plinksrc/ponylink_*.v 14 | iverilog -o $(1).exe $(1).v ../plinksrc/ponylink_test.v ../plinksrc/ponylink_master.v ../plinksrc/ponylink_slave.v ../plinksrc/ponylink_pack.v ../plinksrc/ponylink_txrx.v ../plinksrc/ponylink_8b10b.v ../plinksrc/ponylink_crc32.v 15 | $(1).vcd: $(1).exe 16 | timeout 20 vvp -N $(1).exe +vcd 17 | $(1).ok: $(1).exe 18 | timeout 20 vvp -N $(1).exe 19 | touch $(1).ok 20 | 21 | endef 22 | 23 | $(foreach t,$(tests),$(eval $(call test_tpl,$t))) 24 | 25 | view: 26 | gtkwave testbench.vcd testbench.gtkw 27 | 28 | clean: 29 | rm -f test_[0-9][0-9].v 30 | rm -f test_[0-9][0-9].exe 31 | rm -f test_[0-9][0-9].ok 32 | 33 | -------------------------------------------------------------------------------- /testbench/maketest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from __future__ import division 4 | from __future__ import print_function 5 | from __future__ import absolute_import 6 | 7 | import sys 8 | sys.path.append('../plinksrc') 9 | 10 | from timings import TimingSolver 11 | import numpy as np 12 | # np.random.seed(42) 13 | 14 | do_link_scramble = np.random.choice([True, False]) 15 | 16 | m2s_tdata_width = min(32, np.random.geometric(0.1)); 17 | m2s_tuser_width = min(32, np.random.geometric(0.5) - 1); 18 | 19 | s2m_tdata_width = min(32, np.random.geometric(0.1)); 20 | s2m_tuser_width = min(32, np.random.geometric(0.5) - 1); 21 | 22 | m2s_data = [] 23 | s2m_data = [] 24 | 25 | for i in range(100+np.random.randint(400)): 26 | if not i or np.random.randint(100) < 5: 27 | m2s_data.append([np.random.randint(2 ** m2s_tdata_width), np.random.randint(2 ** m2s_tuser_width), 0]) 28 | else: 29 | m2s_data.append([np.random.randint(2 ** m2s_tdata_width), m2s_data[-1][1], 0]) 30 | if np.random.randint(100) == 0: 31 | m2s_data[-1][2] = 1 32 | 33 | for i in range(100+np.random.randint(400)): 34 | if not i or np.random.randint(100) < 5: 35 | s2m_data.append([np.random.randint(2 ** s2m_tdata_width), np.random.randint(2 ** s2m_tuser_width), 0]) 36 | else: 37 | s2m_data.append([np.random.randint(2 ** s2m_tdata_width), s2m_data[-1][1], 0]) 38 | if np.random.randint(100) == 0: 39 | s2m_data[-1][2] = 1 40 | 41 | while True: 42 | master_parbits = np.random.choice([1, 2, 4]) 43 | slave_parbits = np.random.choice([1, 2, 4]) 44 | 45 | master_period = np.random.randint(5, 20) 46 | slave_period = np.random.randint(5, 20) 47 | 48 | master_pulse_jitter = np.random.uniform(0.01, 0.3 * (master_period / master_parbits)) 49 | slave_pulse_jitter = np.random.uniform(0.01, 0.3 * (slave_period / slave_parbits)) 50 | 51 | print("/* --- TIMING SOLVER REPORT --- **") 52 | print() 53 | print(" master_clk_period = %6.3f" % master_period) 54 | print(" master_bit_period = %6.3f" % (master_period / master_parbits)) 55 | print(" master_pulse_jitter = %6.3f" % master_pulse_jitter) 56 | print(" master_parbits = %d" % master_parbits) 57 | print() 58 | print(" slave_clk_period = %6.3f" % slave_period) 59 | print(" slave_bit_period = %6.3f" % (slave_period / slave_parbits)) 60 | print(" slave_pulse_jitter = %6.3f" % slave_pulse_jitter) 61 | print(" slave_parbits = %d" % slave_parbits) 62 | print() 63 | 64 | solver = TimingSolver() 65 | solver.find_config(0, master_period / master_parbits, slave_period / slave_parbits, master_pulse_jitter) 66 | solver.find_config(1, slave_period / slave_parbits, master_period / master_parbits, slave_pulse_jitter) 67 | 68 | print() 69 | print("** --- TIMING SOLVER REPORT --- */") 70 | print() 71 | 72 | all_configs_ok = True 73 | for v in solver.results["M2S_TT"] + solver.results["S2M_TT"] + solver.results["M2S_ST"] + solver.results["S2M_ST"]: 74 | if v > 255: all_configs_ok = False 75 | 76 | if all_configs_ok: 77 | break 78 | 79 | print("// A timing parameter is out of range: restart random timing generation") 80 | 81 | print("`define m2s_tdata_width %d" % m2s_tdata_width) 82 | print("`define m2s_tuser_width %d" % m2s_tuser_width) 83 | print("`define s2m_tdata_width %d" % s2m_tdata_width) 84 | print("`define s2m_tuser_width %d" % s2m_tuser_width) 85 | 86 | print("`define master_clk_period_ns %.2f" % master_period) 87 | print("`define slave_clk_period_ns %.2f" % slave_period) 88 | 89 | print("`define master_bit_period_ns %.2f" % (master_period / master_parbits)) 90 | print("`define slave_bit_period_ns %.2f" % (slave_period / slave_parbits)) 91 | 92 | print("`define master_pulse_jitter_ns %.2f" % master_pulse_jitter) 93 | print("`define slave_pulse_jitter_ns %.2f" % slave_pulse_jitter) 94 | 95 | print("`define master_parbits %d" % master_parbits) 96 | print("`define slave_parbits %d" % slave_parbits) 97 | 98 | print("`define master_timings 80'h%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" % ( 99 | solver.results["M2S_TT"][4], solver.results["M2S_TT"][3], solver.results["M2S_TT"][2], solver.results["M2S_TT"][1], solver.results["M2S_TT"][0], 100 | solver.results["S2M_ST"][4], solver.results["S2M_ST"][3], solver.results["S2M_ST"][2], solver.results["S2M_ST"][1], solver.results["S2M_ST"][0])) 101 | 102 | print("`define slave_timings 80'h%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" % ( 103 | solver.results["S2M_TT"][4], solver.results["S2M_TT"][3], solver.results["S2M_TT"][2], solver.results["S2M_TT"][1], solver.results["S2M_TT"][0], 104 | solver.results["M2S_ST"][4], solver.results["M2S_ST"][3], solver.results["M2S_ST"][2], solver.results["M2S_ST"][1], solver.results["M2S_ST"][0])) 105 | 106 | print(""" 107 | `timescale 1 ns / 1 ps 108 | 109 | module testbench; 110 | reg master_clk, slave_clk; 111 | reg master_resetn = 0; 112 | wire slave_resetn; 113 | 114 | wire master_linkerror; 115 | wire master_linkready; 116 | 117 | wire slave_linkerror; 118 | wire slave_linkready; 119 | 120 | reg [`m2s_tdata_width-1:0] master_in_tdata = 0; 121 | reg [`m2s_tuser_width-1:0] master_in_tuser = 0; 122 | reg master_in_tlast = 0; 123 | reg master_in_tvalid = 0; 124 | wire master_in_tready; 125 | 126 | wire [`s2m_tdata_width-1:0] master_out_tdata; 127 | wire [`s2m_tuser_width-1:0] master_out_tuser; 128 | wire master_out_tlast; 129 | wire master_out_tvalid; 130 | reg master_out_tready = 0; 131 | 132 | reg [`s2m_tdata_width-1:0] slave_in_tdata = 0; 133 | reg [`s2m_tuser_width-1:0] slave_in_tuser = 0; 134 | reg slave_in_tlast = 0; 135 | reg slave_in_tvalid = 0; 136 | wire slave_in_tready; 137 | 138 | wire [`m2s_tdata_width-1:0] slave_out_tdata; 139 | wire [`m2s_tuser_width-1:0] slave_out_tuser; 140 | wire slave_out_tlast; 141 | wire slave_out_tvalid; 142 | reg slave_out_tready = 0; 143 | 144 | reg [7:0] master_gpio_in = 0; 145 | wire [7:0] master_gpio_out; 146 | reg [7:0] slave_gpio_in = 0; 147 | wire [7:0] slave_gpio_out; 148 | 149 | reg link_scramble_m = 0; 150 | reg link_scramble_s = 0; 151 | reg link_scramble_idle = 0; 152 | wire link_scramble = link_scramble_m || link_scramble_s; 153 | wire link_collision; 154 | 155 | initial begin 156 | if ($test$plusargs("vcd")) begin 157 | $dumpfile("testbench.vcd"); 158 | $dumpvars(0, testbench); 159 | end 160 | end 161 | 162 | ponylink_test #( 163 | .M2S_TDATA_WIDTH(`m2s_tdata_width), 164 | .M2S_TUSER_WIDTH(`m2s_tuser_width), 165 | .S2M_TDATA_WIDTH(`s2m_tdata_width), 166 | .S2M_TUSER_WIDTH(`s2m_tuser_width), 167 | .MASTER_PARBITS(`master_parbits), 168 | .MASTER_TIMINGS(`master_timings), 169 | .SLAVE_PARBITS(`slave_parbits), 170 | .SLAVE_TIMINGS(`slave_timings), 171 | .MASTER_BIT_PERIOD_NS(`master_bit_period_ns), 172 | .SLAVE_BIT_PERIOD_NS(`slave_bit_period_ns), 173 | .MASTER_PULSE_JITTER_NS(`master_pulse_jitter_ns), 174 | .SLAVE_PULSE_JITTER_NS(`slave_pulse_jitter_ns) 175 | ) tester ( 176 | .master_clk(master_clk), 177 | .master_resetn(master_resetn), 178 | .master_linkerror(master_linkerror), 179 | .master_linkready(master_linkready), 180 | 181 | .master_gpio_i(master_gpio_in), 182 | .master_gpio_o(master_gpio_out), 183 | 184 | .master_in_tdata(master_in_tdata), 185 | .master_in_tuser(master_in_tuser), 186 | .master_in_tlast(master_in_tlast), 187 | .master_in_tvalid(master_in_tvalid), 188 | .master_in_tready(master_in_tready), 189 | 190 | .master_out_tdata(master_out_tdata), 191 | .master_out_tuser(master_out_tuser), 192 | .master_out_tlast(master_out_tlast), 193 | .master_out_tvalid(master_out_tvalid), 194 | .master_out_tready(master_out_tready), 195 | 196 | .slave_clk(slave_clk), 197 | .slave_resetn(slave_resetn), 198 | .slave_linkerror(slave_linkerror), 199 | .slave_linkready(slave_linkready), 200 | 201 | .slave_gpio_i(slave_gpio_in), 202 | .slave_gpio_o(slave_gpio_out), 203 | 204 | .slave_in_tdata(slave_in_tdata), 205 | .slave_in_tuser(slave_in_tuser), 206 | .slave_in_tlast(slave_in_tlast), 207 | .slave_in_tvalid(slave_in_tvalid), 208 | .slave_in_tready(slave_in_tready), 209 | 210 | .slave_out_tdata(slave_out_tdata), 211 | .slave_out_tuser(slave_out_tuser), 212 | .slave_out_tlast(slave_out_tlast), 213 | .slave_out_tvalid(slave_out_tvalid), 214 | .slave_out_tready(slave_out_tready), 215 | 216 | .link_scramble(link_scramble), 217 | .link_scramble_idle(link_scramble_idle), 218 | .link_collision(link_collision) 219 | ); 220 | 221 | always begin 222 | master_clk <= 1; #(`master_clk_period_ns / 2.0); 223 | master_clk <= 0; #(`master_clk_period_ns / 2.0); 224 | end 225 | 226 | always begin 227 | slave_clk <= 1; #(`slave_clk_period_ns / 2.0); 228 | slave_clk <= 0; #(`slave_clk_period_ns / 2.0); 229 | end 230 | 231 | reg ok_m2s_send = 0; 232 | reg ok_m2s_recv = 0; 233 | reg ok_s2m_send = 0; 234 | reg ok_s2m_recv = 0; 235 | reg ok_gpio = 0; 236 | 237 | initial begin 238 | repeat (100) @(posedge master_clk); 239 | master_resetn <= 1; 240 | while (!(&{ok_m2s_send, ok_m2s_recv, ok_s2m_send, ok_s2m_recv, ok_gpio})) @(posedge master_clk); 241 | repeat (100) @(posedge master_clk); 242 | $display("OK!"); 243 | @(posedge master_clk); 244 | $finish; 245 | end 246 | 247 | initial begin 248 | @(posedge master_resetn); 249 | @(posedge slave_resetn); 250 | @(posedge link_collision); 251 | $display("Detected link collision at %t.", $time); 252 | repeat (10) @(posedge master_clk); 253 | repeat (10) @(posedge master_clk); 254 | $stop; 255 | end 256 | """) 257 | 258 | gpio_xor = np.random.randint(256); 259 | 260 | print(" always @(posedge slave_clk)") 261 | print(" slave_gpio_in <= slave_gpio_out ^ %d;" % gpio_xor) 262 | 263 | print(" initial begin") 264 | print(" @(posedge master_resetn);") 265 | print(" repeat (100) @(posedge master_clk);") 266 | for i in range(np.random.randint(100)): 267 | if np.random.randint(4) == 0: 268 | v = 0 if np.random.randint(2) else gpio_xor 269 | else: 270 | v = np.random.randint(256) 271 | print(" master_gpio_in <= %d;" % v) 272 | print(" while (master_gpio_out !== %d) @(posedge master_clk);" % (v ^ gpio_xor)) 273 | print(" $display(\"Finished GPIO test.\");") 274 | print(" ok_gpio <= 1;") 275 | print(" end") 276 | 277 | scramble_m_countdown = np.random.randint(len(s2m_data)) 278 | scramble_s_countdown = np.random.randint(len(m2s_data)) 279 | 280 | if do_link_scramble: 281 | print(" initial begin") 282 | print(" link_scramble_idle = 1;") 283 | print(" end") 284 | 285 | print(" initial begin") 286 | print(" @(posedge link_scramble_m);") 287 | print(" repeat (%d) @(posedge master_clk);" % np.random.randint(np.random.choice([10, 20, 50, 100, 200]))) 288 | print(" link_scramble_m = 0;") 289 | print(" end") 290 | 291 | print(" initial begin") 292 | print(" @(posedge link_scramble_s);") 293 | print(" repeat (%d) @(posedge slave_clk);" % np.random.randint(np.random.choice([10, 20, 50, 100, 200]))) 294 | print(" link_scramble_s = 0;") 295 | print(" end") 296 | 297 | else: 298 | print(" always @(posedge master_clk) begin") 299 | print(" if (master_linkerror === 1) begin") 300 | print(" $display(\"Master detected link error at %t.\", $time);") 301 | print(" repeat (20) @(posedge master_clk);") 302 | print(" $stop;") 303 | print(" end") 304 | print(" end") 305 | 306 | print(" always @(posedge slave_clk) begin") 307 | print(" if (slave_linkerror === 1) begin") 308 | print(" $display(\"Slave detected link error at %t.\", $time);") 309 | print(" repeat (20) @(posedge slave_clk);") 310 | print(" $stop;") 311 | print(" end") 312 | print(" end") 313 | 314 | print(" initial begin") 315 | print(" @(posedge master_resetn);") 316 | print(" repeat (100) @(posedge master_clk);") 317 | if np.random.choice([True, False]): 318 | print(" while (!master_linkready) @(posedge master_clk);") 319 | print(" master_in_tvalid <= 1;") 320 | for d in m2s_data: 321 | print(" master_in_tlast <= %d;" % d[2]) 322 | print(" master_in_tuser <= %d;" % d[1]) 323 | print(" master_in_tdata <= %d;" % d[0]) 324 | print(" @(posedge master_clk);") 325 | print(" while (!master_in_tready) @(posedge master_clk);") 326 | print(" master_in_tvalid <= 0;") 327 | print(" $display(\"Finished sending M2S data.\");") 328 | print(" ok_m2s_send <= 1;") 329 | print(" end") 330 | 331 | print(" initial begin") 332 | print(" @(negedge slave_resetn);") 333 | print(" @(posedge slave_resetn);") 334 | print(" repeat (100) @(posedge slave_clk);") 335 | if do_link_scramble or np.random.choice([True, False]): 336 | print(" while (!slave_linkready) @(posedge slave_clk);") 337 | print(" slave_in_tvalid <= 1;") 338 | for d in s2m_data: 339 | print(" slave_in_tlast <= %d;" % d[2]) 340 | print(" slave_in_tuser <= %d;" % d[1]) 341 | print(" slave_in_tdata <= %d;" % d[0]) 342 | print(" @(posedge slave_clk);") 343 | print(" while (!slave_in_tready) @(posedge slave_clk);") 344 | print(" slave_in_tvalid <= 0;") 345 | print(" $display(\"Finished sending S2M data.\");") 346 | print(" ok_s2m_send <= 1;") 347 | print(" end") 348 | 349 | print(" initial begin") 350 | print(" @(negedge slave_resetn);") 351 | print(" @(posedge slave_resetn);") 352 | if np.random.choice([True, False]): 353 | print(" while (!slave_linkready) @(posedge slave_clk);") 354 | print(" slave_out_tready <= 1;") 355 | for d in m2s_data: 356 | if do_link_scramble: 357 | if scramble_s_countdown == 0: 358 | print(" link_scramble_s = 1;") 359 | scramble_s_countdown -= 1 360 | print(" @(posedge slave_clk);") 361 | print(" while (!slave_out_tvalid) @(posedge slave_clk);") 362 | if m2s_tuser_width == 0: 363 | print(" if (slave_out_tlast !== %d || slave_out_tdata !== %d) begin" % (d[2], d[0])) 364 | print(" $display(\"M2S: Expected %%b::%%b, got %%b::%%b\", 1'd%d, %d'd%d, slave_out_tlast, slave_out_tdata);" % (d[2], m2s_tdata_width, d[0])) 365 | else: 366 | print(" if (slave_out_tlast !== %d || slave_out_tuser !== %d || slave_out_tdata !== %d) begin" % (d[2], d[1], d[0])) 367 | print(" $display(\"M2S: Expected %%b:%%b:%%b, got %%b:%%b:%%b\", 1'd%d, %d'd%d, %d'd%d, slave_out_tlast, slave_out_tuser, slave_out_tdata);" % (d[2], m2s_tuser_width, d[1], m2s_tdata_width, d[0])) 368 | print(" repeat (5) @(posedge slave_clk);") 369 | print(" $stop;") 370 | print(" end") 371 | print(" slave_out_tready <= 0;") 372 | print(" $display(\"Finished receiving M2S data.\");") 373 | print(" ok_m2s_recv <= 1;") 374 | print(" end") 375 | 376 | print(" initial begin") 377 | print(" @(posedge master_resetn);") 378 | if np.random.choice([True, False]): 379 | print(" while (!master_linkready) @(posedge master_clk);") 380 | print(" master_out_tready <= 1;") 381 | for d in s2m_data: 382 | if do_link_scramble: 383 | if scramble_m_countdown == 0: 384 | print(" link_scramble_m = 1;") 385 | scramble_m_countdown -= 1 386 | print(" @(posedge master_clk);") 387 | print(" while (!master_out_tvalid) @(posedge master_clk);") 388 | if s2m_tuser_width == 0: 389 | print(" if (master_out_tlast !== %d || master_out_tdata !== %d) begin" % (d[2], d[0])) 390 | print(" $display(\"S2M: Expected %%b::%%b, got %%b::%%b\", 1'd%d, %d'd%d, master_out_tlast, master_out_tdata);" % (d[2], s2m_tdata_width, d[0])) 391 | else: 392 | print(" if (master_out_tlast !== %d || master_out_tuser !== %d || master_out_tdata !== %d) begin" % (d[2], d[1], d[0])) 393 | print(" $display(\"S2M: Expected %%b:%%b:%%b, got %%b:%%b:%%b\", 1'd%d, %d'd%d, %d'd%d, master_out_tlast, master_out_tuser, master_out_tdata);" % (d[2], s2m_tuser_width, d[1], s2m_tdata_width, d[0])) 394 | print(" repeat (5) @(posedge master_clk);") 395 | print(" $stop;") 396 | print(" end") 397 | print(" master_out_tready <= 0;") 398 | print(" $display(\"Finished receiving S2M data.\");") 399 | print(" ok_s2m_recv <= 1;") 400 | print(" end") 401 | 402 | print("endmodule") 403 | 404 | --------------------------------------------------------------------------------