├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── doc └── mem-map.md ├── no2core.mk ├── rtl ├── e1_buf_if_wb.v ├── e1_crc4.v ├── e1_rx.v ├── e1_rx_clock_recovery.v ├── e1_rx_deframer.v ├── e1_rx_filter.v ├── e1_rx_liu.v ├── e1_rx_phy.v ├── e1_tx.v ├── e1_tx_framer.v ├── e1_tx_liu.v ├── e1_tx_phy.v ├── e1_wb.v ├── e1_wb_rx.v ├── e1_wb_tx.v ├── hdb3_dec.v └── hdb3_enc.v └── sim ├── e1_buf_if_wb_tb.v ├── e1_crc4_tb.v ├── e1_tb.v ├── e1_tx_framer_tb.v └── hdb3_tb.v /.gitignore: -------------------------------------------------------------------------------- 1 | build-tmp 2 | __pycache__ 3 | *.vcd 4 | .*.swp 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CERN Open Hardware Licence Version 2 - Weakly Reciprocal 2 | 3 | 4 | Preamble 5 | 6 | CERN has developed this licence to promote collaboration among 7 | hardware designers and to provide a legal tool which supports the 8 | freedom to use, study, modify, share and distribute hardware designs 9 | and products based on those designs. Version 2 of the CERN Open 10 | Hardware Licence comes in three variants: CERN-OHL-P (permissive); and 11 | two reciprocal licences: this licence, CERN-OHL-W (weakly reciprocal) 12 | and CERN-OHL-S (strongly reciprocal). 13 | 14 | The CERN-OHL-W is copyright CERN 2020. Anyone is welcome to use it, in 15 | unmodified form only. 16 | 17 | Use of this Licence does not imply any endorsement by CERN of any 18 | Licensor or their designs nor does it imply any involvement by CERN in 19 | their development. 20 | 21 | 22 | 1 Definitions 23 | 24 | 1.1 'Licence' means this CERN-OHL-W. 25 | 26 | 1.2 'Compatible Licence' means 27 | 28 | a) any earlier version of the CERN Open Hardware licence, or 29 | 30 | b) any version of the CERN-OHL-S or the CERN-OHL-W, or 31 | 32 | c) any licence which permits You to treat the Source to which 33 | it applies as licensed under CERN-OHL-S or CERN-OHL-W 34 | provided that on Conveyance of any such Source, or any 35 | associated Product You treat the Source in question as being 36 | licensed under CERN-OHL-S or CERN-OHL-W as appropriate. 37 | 38 | 1.3 'Source' means information such as design materials or digital 39 | code which can be applied to Make or test a Product or to 40 | prepare a Product for use, Conveyance or sale, regardless of its 41 | medium or how it is expressed. It may include Notices. 42 | 43 | 1.4 'Covered Source' means Source that is explicitly made available 44 | under this Licence. 45 | 46 | 1.5 'Product' means any device, component, work or physical object, 47 | whether in finished or intermediate form, arising from the use, 48 | application or processing of Covered Source. 49 | 50 | 1.6 'Make' means to create or configure something, whether by 51 | manufacture, assembly, compiling, loading or applying Covered 52 | Source or another Product or otherwise. 53 | 54 | 1.7 'Available Component' means any part, sub-assembly, library or 55 | code which: 56 | 57 | a) is licensed to You as Complete Source under a Compatible 58 | Licence; or 59 | 60 | b) is available, at the time a Product or the Source containing 61 | it is first Conveyed, to You and any other prospective 62 | licensees 63 | 64 | i) with sufficient rights and information (including any 65 | configuration and programming files and information 66 | about its characteristics and interfaces) to enable it 67 | either to be Made itself, or to be sourced and used to 68 | Make the Product; or 69 | ii) as part of the normal distribution of a tool used to 70 | design or Make the Product. 71 | 72 | 1.8 'External Material' means anything (including Source) which: 73 | 74 | a) is only combined with Covered Source in such a way that it 75 | interfaces with the Covered Source using a documented 76 | interface which is described in the Covered Source; and 77 | 78 | b) is not a derivative of or contains Covered Source, or, if it 79 | is, it is solely to the extent necessary to facilitate such 80 | interfacing. 81 | 82 | 1.9 'Complete Source' means the set of all Source necessary to Make 83 | a Product, in the preferred form for making modifications, 84 | including necessary installation and interfacing information 85 | both for the Product, and for any included Available Components. 86 | If the format is proprietary, it must also be made available in 87 | a format (if the proprietary tool can create it) which is 88 | viewable with a tool available to potential licensees and 89 | licensed under a licence approved by the Free Software 90 | Foundation or the Open Source Initiative. Complete Source need 91 | not include the Source of any Available Component, provided that 92 | You include in the Complete Source sufficient information to 93 | enable a recipient to Make or source and use the Available 94 | Component to Make the Product. 95 | 96 | 1.10 'Source Location' means a location where a Licensor has placed 97 | Covered Source, and which that Licensor reasonably believes will 98 | remain easily accessible for at least three years for anyone to 99 | obtain a digital copy. 100 | 101 | 1.11 'Notice' means copyright, acknowledgement and trademark notices, 102 | Source Location references, modification notices (subsection 103 | 3.3(b)) and all notices that refer to this Licence and to the 104 | disclaimer of warranties that are included in the Covered 105 | Source. 106 | 107 | 1.12 'Licensee' or 'You' means any person exercising rights under 108 | this Licence. 109 | 110 | 1.13 'Licensor' means a natural or legal person who creates or 111 | modifies Covered Source. A person may be a Licensee and a 112 | Licensor at the same time. 113 | 114 | 1.14 'Convey' means to communicate to the public or distribute. 115 | 116 | 117 | 2 Applicability 118 | 119 | 2.1 This Licence governs the use, copying, modification, Conveying 120 | of Covered Source and Products, and the Making of Products. By 121 | exercising any right granted under this Licence, You irrevocably 122 | accept these terms and conditions. 123 | 124 | 2.2 This Licence is granted by the Licensor directly to You, and 125 | shall apply worldwide and without limitation in time. 126 | 127 | 2.3 You shall not attempt to restrict by contract or otherwise the 128 | rights granted under this Licence to other Licensees. 129 | 130 | 2.4 This Licence is not intended to restrict fair use, fair dealing, 131 | or any other similar right. 132 | 133 | 134 | 3 Copying, Modifying and Conveying Covered Source 135 | 136 | 3.1 You may copy and Convey verbatim copies of Covered Source, in 137 | any medium, provided You retain all Notices. 138 | 139 | 3.2 You may modify Covered Source, other than Notices, provided that 140 | You irrevocably undertake to make that modified Covered Source 141 | available from a Source Location should You Convey a Product in 142 | circumstances where the recipient does not otherwise receive a 143 | copy of the modified Covered Source. In each case subsection 3.3 144 | shall apply. 145 | 146 | You may only delete Notices if they are no longer applicable to 147 | the corresponding Covered Source as modified by You and You may 148 | add additional Notices applicable to Your modifications. 149 | 150 | 3.3 You may Convey modified Covered Source (with the effect that You 151 | shall also become a Licensor) provided that You: 152 | 153 | a) retain Notices as required in subsection 3.2; 154 | 155 | b) add a Notice to the modified Covered Source stating that You 156 | have modified it, with the date and brief description of how 157 | You have modified it; 158 | 159 | c) add a Source Location Notice for the modified Covered Source 160 | if You Convey in circumstances where the recipient does not 161 | otherwise receive a copy of the modified Covered Source; and 162 | 163 | d) license the modified Covered Source under the terms and 164 | conditions of this Licence (or, as set out in subsection 165 | 8.3, a later version, if permitted by the licence of the 166 | original Covered Source). Such modified Covered Source must 167 | be licensed as a whole, but excluding Available Components 168 | contained in it or External Material to which it is 169 | interfaced, which remain licensed under their own applicable 170 | licences. 171 | 172 | 173 | 4 Making and Conveying Products 174 | 175 | 4.1 You may Make Products, and/or Convey them, provided that You 176 | either provide each recipient with a copy of the Complete Source 177 | or ensure that each recipient is notified of the Source Location 178 | of the Complete Source. That Complete Source includes Covered 179 | Source and You must accordingly satisfy Your obligations set out 180 | in subsection 3.3. If specified in a Notice, the Product must 181 | visibly and securely display the Source Location on it or its 182 | packaging or documentation in the manner specified in that 183 | Notice. 184 | 185 | 4.2 Where You Convey a Product which incorporates External Material, 186 | the Complete Source for that Product which You are required to 187 | provide under subsection 4.1 need not include any Source for the 188 | External Material. 189 | 190 | 4.3 You may license Products under terms of Your choice, provided 191 | that such terms do not restrict or attempt to restrict any 192 | recipients' rights under this Licence to the Covered Source. 193 | 194 | 195 | 5 Research and Development 196 | 197 | You may Convey Covered Source, modified Covered Source or Products to 198 | a legal entity carrying out development, testing or quality assurance 199 | work on Your behalf provided that the work is performed on terms which 200 | prevent the entity from both using the Source or Products for its own 201 | internal purposes and Conveying the Source or Products or any 202 | modifications to them to any person other than You. Any modifications 203 | made by the entity shall be deemed to be made by You pursuant to 204 | subsection 3.2. 205 | 206 | 207 | 6 DISCLAIMER AND LIABILITY 208 | 209 | 6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products 210 | are provided 'as is' and any express or implied warranties, 211 | including, but not limited to, implied warranties of 212 | merchantability, of satisfactory quality, non-infringement of 213 | third party rights, and fitness for a particular purpose or use 214 | are disclaimed in respect of any Source or Product to the 215 | maximum extent permitted by law. The Licensor makes no 216 | representation that any Source or Product does not or will not 217 | infringe any patent, copyright, trade secret or other 218 | proprietary right. The entire risk as to the use, quality, and 219 | performance of any Source or Product shall be with You and not 220 | the Licensor. This disclaimer of warranty is an essential part 221 | of this Licence and a condition for the grant of any rights 222 | granted under this Licence. 223 | 224 | 6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to 225 | the maximum extent permitted by law, have no liability for 226 | direct, indirect, special, incidental, consequential, exemplary, 227 | punitive or other damages of any character including, without 228 | limitation, procurement of substitute goods or services, loss of 229 | use, data or profits, or business interruption, however caused 230 | and on any theory of contract, warranty, tort (including 231 | negligence), product liability or otherwise, arising in any way 232 | in relation to the Covered Source, modified Covered Source 233 | and/or the Making or Conveyance of a Product, even if advised of 234 | the possibility of such damages, and You shall hold the 235 | Licensor(s) free and harmless from any liability, costs, 236 | damages, fees and expenses, including claims by third parties, 237 | in relation to such use. 238 | 239 | 240 | 7 Patents 241 | 242 | 7.1 Subject to the terms and conditions of this Licence, each 243 | Licensor hereby grants to You a perpetual, worldwide, 244 | non-exclusive, no-charge, royalty-free, irrevocable (except as 245 | stated in subsections 7.2 and 8.4) patent license to Make, have 246 | Made, use, offer to sell, sell, import, and otherwise transfer 247 | the Covered Source and Products, where such licence applies only 248 | to those patent claims licensable by such Licensor that are 249 | necessarily infringed by exercising rights under the Covered 250 | Source as Conveyed by that Licensor. 251 | 252 | 7.2 If You institute patent litigation against any entity (including 253 | a cross-claim or counterclaim in a lawsuit) alleging that the 254 | Covered Source or a Product constitutes direct or contributory 255 | patent infringement, or You seek any declaration that a patent 256 | licensed to You under this Licence is invalid or unenforceable 257 | then any rights granted to You under this Licence shall 258 | terminate as of the date such process is initiated. 259 | 260 | 261 | 8 General 262 | 263 | 8.1 If any provisions of this Licence are or subsequently become 264 | invalid or unenforceable for any reason, the remaining 265 | provisions shall remain effective. 266 | 267 | 8.2 You shall not use any of the name (including acronyms and 268 | abbreviations), image, or logo by which the Licensor or CERN is 269 | known, except where needed to comply with section 3, or where 270 | the use is otherwise allowed by law. Any such permitted use 271 | shall be factual and shall not be made so as to suggest any kind 272 | of endorsement or implication of involvement by the Licensor or 273 | its personnel. 274 | 275 | 8.3 CERN may publish updated versions and variants of this Licence 276 | which it considers to be in the spirit of this version, but may 277 | differ in detail to address new problems or concerns. New 278 | versions will be published with a unique version number and a 279 | variant identifier specifying the variant. If the Licensor has 280 | specified that a given variant applies to the Covered Source 281 | without specifying a version, You may treat that Covered Source 282 | as being released under any version of the CERN-OHL with that 283 | variant. If no variant is specified, the Covered Source shall be 284 | treated as being released under CERN-OHL-S. The Licensor may 285 | also specify that the Covered Source is subject to a specific 286 | version of the CERN-OHL or any later version in which case You 287 | may apply this or any later version of CERN-OHL with the same 288 | variant identifier published by CERN. 289 | 290 | You may treat Covered Source licensed under CERN-OHL-W as 291 | licensed under CERN-OHL-S if and only if all Available 292 | Components referenced in the Covered Source comply with the 293 | corresponding definition of Available Component for CERN-OHL-S. 294 | 295 | 8.4 This Licence shall terminate with immediate effect if You fail 296 | to comply with any of its terms and conditions. 297 | 298 | 8.5 However, if You cease all breaches of this Licence, then Your 299 | Licence from any Licensor is reinstated unless such Licensor has 300 | terminated this Licence by giving You, while You remain in 301 | breach, a notice specifying the breach and requiring You to cure 302 | it within 30 days, and You have failed to come into compliance 303 | in all material respects by the end of the 30 day period. Should 304 | You repeat the breach after receipt of a cure notice and 305 | subsequent reinstatement, this Licence will terminate 306 | immediately and permanently. Section 6 shall continue to apply 307 | after any termination. 308 | 309 | 8.6 This Licence shall not be enforceable except by a Licensor 310 | acting as such, and third party beneficiary rights are 311 | specifically excluded. 312 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CORE := no2e1 2 | 3 | NO2BUILD_DIR ?= $(abspath ../../build) 4 | include $(NO2BUILD_DIR)/core-rules.mk 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nitro E1 core 2 | ============= 3 | 4 | This fpga core implements various functions related to E1 interface 5 | used in the telcom world. The relevant specifications here are : 6 | 7 | * [G.702 "Digital Hierarchy Bit Rates"](https://www.itu.int/rec/T-REC-G.702/en) 8 | * [G.703 "Physical/electrical characteristics of hierarchical digital interface"](https://www.itu.int/rec/T-REC-G.703/en) 9 | * [G.704 "Synchronous frame structures used at 1544, 6312, 2048, 8448 and 44 736 kbit/s hierarchical levels"](https://www.itu.int/rec/T-REC-G.704/en) 10 | * [G.706 "FRAME ALIGNMENT AND CYCLIC REDUNDANCY CHECK (CRC) PROCEDURES RELATING TO BASIC FRAME STRUCTURES DEFINED IN RECOMMENDATION G.704"](https://www.itu.int/rec/T-REC-G.706/en) 11 | 12 | Physical interface can either be using a "fake" PHY, basically directly 13 | using FPGA IOs, or using a true LIU. 14 | 15 | On the internal side, some native interface is provided, but a higher 16 | level one is also provided in the form of a wishbone interface. 17 | 18 | Refer to the `doc/` subdirectory for complete core documentation. 19 | 20 | 21 | Limitations 22 | ----------- 23 | 24 | Currently this core was only used on iCE40 and uses some direct `SB_IO` 25 | instances for the IO registers and differential inputs. It would be very 26 | easy to adapt those to other FPGA architectures. 27 | 28 | 29 | License 30 | ------- 31 | 32 | This core is licensed under the 33 | "CERN Open Hardware Licence Version 2 - Weakly Reciprocal" license. 34 | 35 | See LICENSE file for full text. 36 | -------------------------------------------------------------------------------- /doc/mem-map.md: -------------------------------------------------------------------------------- 1 | Nitro E1 Core Memory Map 2 | ======================== 3 | 4 | RX 5 | -- 6 | 7 | ### RX Control (Write Only, addr `(N*4) + 0`) 8 | 9 | ``` 10 | ,---------------------------------------------------------------, 11 | | f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 12 | |---------------------------------------------------------------| 13 | | / | oc| / | mode | e | 14 | '---------------------------------------------------------------' 15 | ``` 16 | 17 | * `oc`: Overflow Clear 18 | * `mode`: 19 | - `00`: Transparent; no alignment at all 20 | - `01`: Byte Alignment 21 | - `10`: Basic Frame Alignment; align to the 32-byte basic frame (No CRC4) 22 | - `11`: Multi Frame Alignment; align to the CRC4 multi-frame 23 | * `e`: Enable the receiver 24 | 25 | Here, _alignment_ is defined in terms of the alignment of the 26 | incoming E1 bitstream versus the start of the receive buffer. In 27 | transparent mode, there is an arbitrary byte + bit offset between 28 | incoming data and the buffer start. In Multi Frame Alignment mode, 29 | the first byte of the buffer is the TS0 byte of the first frame 30 | in a multiframe. 31 | 32 | ### RX Status (Read Only, addr `(N*4) + 0`) 33 | 34 | ``` 35 | ,---------------------------------------------------------------, 36 | | f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 37 | |---------------------------------------------------------------| 38 | | / | o |bof|boe|bif|bie| / | a | e | 39 | '---------------------------------------------------------------' 40 | ``` 41 | 42 | * `o` : Overflow (a multi frame was dropped) 43 | * `bof`: BD Out Full 44 | * `boe`: BD Out Empty 45 | * `bif`: BD In Full 46 | * `bie`: BD In Empty 47 | * 'a' : Aligned; configured alignment has been obtained 48 | * `e` : Receiver is enabled 49 | 50 | 51 | ### RX BD Submit (Write Only, addr `(N*4) + 1`) 52 | 53 | Writes to this location push a buffer descriptor to be filled 54 | with a multiframe by the E1 core. 55 | 56 | ``` 57 | ,---------------------------------------------------------------, 58 | | f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 59 | |---------------------------------------------------------------| 60 | | / | mf | 61 | '---------------------------------------------------------------' 62 | ``` 63 | 64 | * `mf` : Multi-Frame address 65 | 66 | 67 | ### RX BD Status (Read Only, addr `(N*4) + 1`) 68 | 69 | Reads from the location retrieve a buffer descriptor that has been 70 | filled with a multiframe by the E1 core. 71 | 72 | ``` 73 | ,---------------------------------------------------------------, 74 | | f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 75 | |---------------------------------------------------------------| 76 | | v | c1| c0| / | mf | 77 | '---------------------------------------------------------------' 78 | ``` 79 | 80 | * `v` : Valid 81 | * `c1` : CRC status for sub-multi-frame 1 82 | * `c0` : CRC status for sub-multi-frame 0 83 | * `mf` : Multi-Frame address 84 | 85 | Note that just as is the case in the E1 data stream, the CRC 86 | status is `1` = CRC OK and `0` = CRC error. 87 | 88 | 89 | TX 90 | -- 91 | 92 | ### TX Control (Write Only, addr `(N*4) + 2`) 93 | 94 | ``` 95 | ,---------------------------------------------------------------, 96 | | f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 97 | |---------------------------------------------------------------| 98 | | / | uc| / | ls| l | a | t | mode | e | 99 | '---------------------------------------------------------------' 100 | ``` 101 | 102 | * `uc`: Underflow Clear 103 | * `ls`: Loopback Select (0=Local, 1=Cross)\ 104 | If `l` is enabled, this bit selects the source of the external 105 | loop-back data: `0` means data received on the very same port; 106 | `1` means data received on the _other_ port. 107 | * `l` : External Loopback\ 108 | If enabled, transmitter loops back whatever data was received 109 | by the receiver of the port selected by `ls` 110 | * `a` : Alarm (sets Alarm bit on transmitted frames) 111 | * `t` : Timing source (0=internal, 1=lock to RX) 112 | * `mode`: 113 | - `00`: Transparent\ 114 | The Framer does not preform any modification/insertion of bits 115 | into TS0 and just transparently transmits the data as-is 116 | - `01`: TS0 framing, no CRC4\ 117 | The framer generates FAS but not the Si bits (bit 1) 118 | - `10`: TS0 framing, CRC4\ 119 | The framer generates framing patterns on TS0, computes CRC4 120 | and populates the C-bits with it 121 | - `11`: TS0 framing, CRC4 + Auto "E" bits\ 122 | The framer generates framing patterns on TS0, computes CRC4, 123 | populates the C-bits with it and automatically reports 124 | receive-side CRC4 errors in the E-bits 125 | * `e` : Enable the transmitter 126 | 127 | 128 | ### TX Status (Read Only, addr `(N*4) + 2`) 129 | 130 | ``` 131 | ,---------------------------------------------------------------, 132 | | f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 133 | |---------------------------------------------------------------| 134 | | | u |bof|boe|bif|bie| | e | 135 | '---------------------------------------------------------------' 136 | ``` 137 | 138 | * `u` : Underflow (a multi frame was missed) 139 | * `bof`: BD Out Full 140 | * `boe`: BD Out Empty 141 | * `bif`: BD In Full 142 | * `bie`: BD In Empty 143 | * `e` : Transmitter is enabled 144 | 145 | 146 | ### TX BD Submit (Write Only, addr `(N*4) + 3`) 147 | 148 | Writes to this location push a buffer descriptor of a multiframe 149 | to be transmitted by the E1 core. 150 | 151 | ``` 152 | ,---------------------------------------------------------------, 153 | | f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 154 | |---------------------------------------------------------------| 155 | | / | c1| c0| / | mf | 156 | '---------------------------------------------------------------' 157 | ``` 158 | 159 | * `c1` : CRC 'E' bit for sub-multi-frame 1 (only used if tx_mode != `11`) 160 | * `c0` : CRC 'E' bit for sub-multi-frame 0 (only used if tx_mode != `11`) 161 | * `mf` : Multi-Frame address 162 | 163 | 164 | ### TX BD Status (Read Only, addr `(N*4) + 3`) 165 | 166 | Reads from the location retrieve a buffer descriptor of a multiframe 167 | that has been transmitted by the E1 core. 168 | 169 | ``` 170 | ,---------------------------------------------------------------, 171 | | f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 172 | |---------------------------------------------------------------| 173 | | v | / | mf | 174 | '---------------------------------------------------------------' 175 | ``` 176 | 177 | * `v` : Valid 178 | * `mf` : Multi-Frame address 179 | 180 | 181 | Notes 182 | ----- 183 | 184 | * The register description above assumed `MFW` was set to `7` when 185 | instantiating the core. If the value is different, this will be 186 | reflected in the various width of the `mf` fields in the Buffer 187 | Descriptors words. 188 | -------------------------------------------------------------------------------- /no2core.mk: -------------------------------------------------------------------------------- 1 | CORE := no2e1 2 | 3 | RTL_SRCS_no2e1 = $(addprefix rtl/, \ 4 | e1_crc4.v \ 5 | e1_rx_clock_recovery.v \ 6 | e1_rx_deframer.v \ 7 | e1_rx_filter.v \ 8 | e1_rx_phy.v \ 9 | e1_rx_liu.v \ 10 | e1_rx.v \ 11 | e1_tx_framer.v \ 12 | e1_tx_phy.v \ 13 | e1_tx_liu.v \ 14 | e1_tx.v \ 15 | e1_buf_if_wb.v \ 16 | e1_wb_rx.v \ 17 | e1_wb_tx.v \ 18 | e1_wb.v \ 19 | hdb3_dec.v \ 20 | hdb3_enc.v \ 21 | ) 22 | 23 | TESTBENCHES_no2e1 := \ 24 | e1_buf_if_wb_tb \ 25 | e1_crc4_tb \ 26 | e1_tb \ 27 | e1_tx_framer_tb \ 28 | hdb3_tb \ 29 | 30 | include $(NO2BUILD_DIR)/core-magic.mk 31 | -------------------------------------------------------------------------------- /rtl/e1_buf_if_wb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_buf_if_wb.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 buffer interface to wishbone master conversion 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_buf_if_wb #( 15 | parameter integer N = 1, // Number of units 16 | parameter UNIT_HAS_RX = 1'b1, // 1 bit per unit 17 | parameter UNIT_HAS_TX = 1'b1, // 1 bit per unit 18 | parameter integer MFW = 7, // Multi-Frame width 19 | parameter integer DW = 32, // 16 or 32 20 | 21 | // auto-set parameters 22 | parameter integer MW = DW / 8, // Mask Width 23 | parameter integer AW = MFW + 9 - $clog2(MW) // Address Width 24 | )( 25 | // Wishbone master 26 | output reg [AW-1:0] wb_addr, 27 | input wire [DW-1:0] wb_rdata, 28 | output wire [DW-1:0] wb_wdata, 29 | output wire [MW-1:0] wb_wmsk, 30 | output wire wb_cyc, 31 | output wire wb_we, 32 | input wire wb_ack, 33 | 34 | // Buffer interface 35 | // E1 RX (write) 36 | input wire [(N*8) -1:0] buf_rx_data, 37 | input wire [(N*5) -1:0] buf_rx_ts, 38 | input wire [(N*4) -1:0] buf_rx_frame, 39 | input wire [(N*MFW)-1:0] buf_rx_mf, 40 | input wire [ N -1:0] buf_rx_we, 41 | output wire [ N -1:0] buf_rx_rdy, 42 | 43 | // E1 TX (read) 44 | output wire [(N*8) -1:0] buf_tx_data, 45 | input wire [(N*5) -1:0] buf_tx_ts, 46 | input wire [(N*4) -1:0] buf_tx_frame, 47 | input wire [(N*MFW)-1:0] buf_tx_mf, 48 | input wire [ N -1:0] buf_tx_re, 49 | output wire [ N -1:0] buf_tx_rdy, 50 | 51 | // Clock / Reset 52 | input wire clk, 53 | input wire rst 54 | ); 55 | 56 | localparam integer LW = $clog2(MW); // LSB width 57 | localparam integer FW = AW + LW; // Full-Address width 58 | localparam integer CW = $clog2(2*N); // Channel width 59 | 60 | 61 | // Signals 62 | // ------- 63 | 64 | genvar i; 65 | 66 | // RX 67 | reg [ N-1:0] rx_pending; 68 | wire [ N-1:0] rx_done; 69 | reg [ 7:0] rx_data_reg[0:N-1]; 70 | reg [FW-1:0] rx_addr_reg[0:N-1]; 71 | 72 | // TX 73 | reg [ N-1:0] tx_pending; 74 | wire [ N-1:0] tx_done; 75 | reg [ 7:0] tx_data_reg[0:N-1]; 76 | reg [FW-1:0] tx_addr_reg[0:N-1]; 77 | 78 | // Transactions 79 | wire [2*N-1:0] t_pending; 80 | reg [2*N-1:0] t_done; 81 | 82 | reg t_nxt_busy; 83 | reg t_busy; 84 | reg [CW-1:0] t_nxt_chan; 85 | reg [CW-1:0] t_chan; // MSB = RX(0) / TX(1) 86 | 87 | reg [LW-1:0] wb_addr_lsb; 88 | reg [ 7:0] wb_wdata_byte; 89 | wire [ 7:0] wb_rdata_mux; 90 | 91 | 92 | // E1 RX (write) 93 | // ------------- 94 | 95 | generate 96 | for (i=0; i 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_crc4 #( 15 | parameter INIT = 4'h0, 16 | parameter POLY = 4'h3 17 | )( 18 | // Input 19 | input wire in_bit, 20 | input wire in_first, 21 | input wire in_valid, 22 | 23 | // Output (updated 1 cycle after input) 24 | output wire [3:0] out_crc4, 25 | 26 | // Common 27 | input wire clk, 28 | input wire rst 29 | ); 30 | 31 | reg [3:0] state; 32 | wire [3:0] state_fb_mux; 33 | wire [3:0] state_upd_mux; 34 | 35 | assign state_fb_mux = (INIT & {4{in_first}}) | (state & {4{~in_first}}); // in_first ? INIT : state 36 | assign state_upd_mux = (state_fb_mux[3] != in_bit) ? POLY : 0; 37 | 38 | always @(posedge clk) 39 | if (in_valid) 40 | state <= { state_fb_mux[2:0], 1'b0 } ^ state_upd_mux; 41 | 42 | assign out_crc4 = state; 43 | 44 | endmodule // e1_crc4 45 | -------------------------------------------------------------------------------- /rtl/e1_rx.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_rx.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 RX top-level 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_rx #( 15 | parameter integer LIU = 0, 16 | parameter integer MFW = 7 17 | )( 18 | // IO pads 19 | // Raw PHY 20 | input wire pad_rx_hi_p, 21 | input wire pad_rx_hi_n, 22 | input wire pad_rx_lo_p, 23 | input wire pad_rx_lo_n, 24 | 25 | // LIU 26 | input wire pad_rx_data, 27 | input wire pad_rx_clk, 28 | 29 | // Buffer interface 30 | output wire [7:0] buf_data, 31 | output wire [4:0] buf_ts, 32 | output wire [3:0] buf_frame, 33 | output wire [MFW-1:0] buf_mf, 34 | output wire buf_we, 35 | input wire buf_rdy, 36 | 37 | // BD interface 38 | input wire [MFW-1:0] bd_mf, 39 | output reg [1:0] bd_crc_e, 40 | input wire bd_valid, 41 | output reg bd_done, 42 | output reg bd_miss, 43 | 44 | // Loopback output 45 | output wire lb_bit, 46 | output wire lb_valid, 47 | 48 | // Control 49 | input wire ctrl_mode_mf, 50 | 51 | // Status 52 | output wire status_aligned, 53 | 54 | // Monitor pulses 55 | // [2] rx_one Every sampled '1' bit 56 | // [1] rx_sample Every sample 57 | // [0] rx_pulse Every detected pulse 58 | output wire [2:0] mon_tick, 59 | 60 | // Common 61 | input wire clk, 62 | input wire rst 63 | ); 64 | 65 | // Signals 66 | // ------- 67 | 68 | // Low level (input -> bits) 69 | wire ll_raw_hi, ll_raw_lo; 70 | wire ll_flt_hi, ll_flt_lo, ll_flt_stb; 71 | wire ll_cdr_hi, ll_cdr_lo, ll_cdr_stb; 72 | 73 | wire ll_bit; 74 | wire ll_valid; 75 | 76 | // Deframer 77 | wire [7:0] df_data; 78 | wire [3:0] df_frame; 79 | wire [4:0] df_ts; 80 | wire df_ts_is0; 81 | wire df_first; 82 | wire df_last; 83 | wire df_valid; 84 | 85 | wire df_err_crc; 86 | wire df_err_mfa; 87 | wire df_err_fas; 88 | wire df_err_nfas; 89 | 90 | wire df_aligned; 91 | 92 | // Buffer Descriptor handling 93 | reg mf_valid; 94 | 95 | 96 | // Low-level bit recovery 97 | // ---------------------- 98 | 99 | generate 100 | if (LIU == 0) begin 101 | 102 | // PHY 103 | e1_rx_phy phy_I ( 104 | .pad_rx_hi_p(pad_rx_hi_p), 105 | .pad_rx_hi_n(pad_rx_hi_n), 106 | .pad_rx_lo_p(pad_rx_lo_p), 107 | .pad_rx_lo_n(pad_rx_lo_n), 108 | .rx_hi(ll_raw_hi), 109 | .rx_lo(ll_raw_lo), 110 | .clk(clk), 111 | .rst(rst) 112 | ); 113 | 114 | // Glitch filtering 115 | e1_rx_filter filter_I ( 116 | .in_hi(ll_raw_hi), 117 | .in_lo(ll_raw_lo), 118 | .out_hi(ll_flt_hi), 119 | .out_lo(ll_flt_lo), 120 | .out_stb(ll_flt_stb), 121 | .clk(clk), 122 | .rst(rst) 123 | ); 124 | 125 | // Clock recovery 126 | e1_rx_clock_recovery clock_I ( 127 | .in_hi(ll_flt_hi), 128 | .in_lo(ll_flt_lo), 129 | .in_stb(ll_flt_stb), 130 | .out_hi(ll_cdr_hi), 131 | .out_lo(ll_cdr_lo), 132 | .out_stb(ll_cdr_stb), 133 | .clk(clk), 134 | .rst(rst) 135 | ); 136 | 137 | // HDB3 decoding 138 | hdb3_dec hdb3_I ( 139 | .in_pos(ll_cdr_hi), 140 | .in_neg(ll_cdr_lo), 141 | .in_valid(ll_cdr_stb), 142 | .out_data(ll_bit), 143 | .out_valid(ll_valid), 144 | .clk(clk), 145 | .rst(rst) 146 | ); 147 | 148 | end else begin 149 | 150 | // LIU interface 151 | e1_rx_liu liuif_I ( 152 | .pad_rx_data(pad_rx_data), 153 | .pad_rx_clk(pad_rx_clk), 154 | .out_data(ll_bit), 155 | .out_valid(ll_valid), 156 | .clk(clk), 157 | .rst(rst) 158 | ); 159 | 160 | // Dummy for monitor ticks 161 | assign ll_flt_stb = 1'b0; 162 | 163 | end 164 | endgenerate 165 | 166 | // Loopback output 167 | assign lb_bit = ll_bit; 168 | assign lb_valid = ll_valid; 169 | 170 | 171 | // High-level frame recovery 172 | // ------------------------- 173 | 174 | // Deframer 175 | e1_rx_deframer deframer_I ( 176 | .in_bit(ll_bit), 177 | .in_valid(ll_valid), 178 | .out_data(df_data), 179 | .out_frame(df_frame), 180 | .out_ts(df_ts), 181 | .out_ts_is0(df_ts_is0), 182 | .out_first(df_first), 183 | .out_last(df_last), 184 | .out_valid(df_valid), 185 | .out_err_crc(df_err_crc), 186 | .out_err_mfa(df_err_mfa), 187 | .out_err_fas(df_err_fas), 188 | .out_err_nfas(df_err_nfas), 189 | .aligned(df_aligned), 190 | .ctrl_mode_mf(ctrl_mode_mf), 191 | .clk(clk), 192 | .rst(rst) 193 | ); 194 | 195 | // Buffer Descriptor 196 | // Keep track if we have a valid MF capture 197 | always @(posedge clk or posedge rst) 198 | if (rst) 199 | mf_valid <= 1'b0; 200 | else 201 | mf_valid <= ((df_valid & df_first) ? bd_valid : mf_valid) & df_aligned; 202 | 203 | // We register those because a 1 cycle delay doesn't matter 204 | // (we won't get another byte write for ~ 120 cycle) 205 | always @(posedge clk) 206 | begin 207 | bd_done <= df_valid & df_last & mf_valid; 208 | bd_miss <= df_valid & df_first & ~bd_valid; 209 | end 210 | 211 | // Track the CRC status of the two SMF 212 | always @(posedge clk or posedge rst) 213 | if (rst) 214 | bd_crc_e <= 2'b11; 215 | else 216 | bd_crc_e <= (bd_done) ? 2'b11 : (bd_crc_e & ~{ 217 | df_valid & df_err_crc & df_frame[3], // CRC error in second SMF 218 | df_valid & df_err_crc & ~df_frame[3] // CRC error in first SMF 219 | }); 220 | 221 | // Buffer write 222 | assign buf_data = df_data; 223 | assign buf_ts = df_ts; 224 | assign buf_frame = df_frame; 225 | assign buf_mf = bd_mf; 226 | assign buf_we = df_valid & bd_valid; 227 | 228 | // Status output 229 | assign status_aligned = df_aligned; 230 | 231 | // Monitoring tick 232 | assign mon_tick = { 233 | ll_valid & ll_bit, 234 | ll_valid, 235 | ll_flt_stb 236 | }; 237 | 238 | endmodule // e1_rx 239 | -------------------------------------------------------------------------------- /rtl/e1_rx_clock_recovery.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_rx_clock_recovery.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 Clock recovery/sampling 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_rx_clock_recovery ( 15 | // Input 16 | input wire in_hi, 17 | input wire in_lo, 18 | input wire in_stb, 19 | 20 | // Output 21 | output wire out_hi, 22 | output wire out_lo, 23 | output wire out_stb, 24 | 25 | // Common 26 | input wire clk, 27 | input wire rst 28 | ); 29 | 30 | reg [5:0] cnt; 31 | 32 | always @(posedge clk) 33 | begin 34 | if (rst) 35 | cnt <= 5'h0f; 36 | else begin 37 | if (in_stb) 38 | cnt <= 5'h01; 39 | else if (cnt[5]) 40 | cnt <= 5'h0d; 41 | else 42 | cnt <= cnt - 1; 43 | end 44 | end 45 | 46 | assign out_hi = in_hi; 47 | assign out_lo = in_lo; 48 | assign out_stb = cnt[5]; 49 | 50 | endmodule // e1_rx_clock_recovery 51 | -------------------------------------------------------------------------------- /rtl/e1_rx_deframer.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_rx_deframer.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 Frame alignement recovery and checking as described G.706 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_rx_deframer #( 15 | parameter integer TS0_START = 0 16 | )( 17 | // Input 18 | input wire in_bit, 19 | input wire in_valid, 20 | 21 | // Output 22 | output reg [7:0] out_data, 23 | output reg [3:0] out_frame, 24 | output reg [4:0] out_ts, 25 | output reg out_ts_is0, 26 | output reg out_first, 27 | output reg out_last, 28 | output reg out_valid, 29 | 30 | output wire out_err_crc, 31 | output wire out_err_mfa, 32 | output wire out_err_fas, 33 | output wire out_err_nfas, 34 | 35 | output reg aligned, 36 | 37 | // Control 38 | input wire ctrl_mode_mf, 39 | 40 | // Common 41 | input wire clk, 42 | input wire rst 43 | ); 44 | 45 | // FSM defines 46 | // ----------- 47 | 48 | localparam 49 | ST_FRAME_SEARCH = 0, 50 | ST_FRAME_VALIDATE = 1, 51 | ST_MULTIFRAME_SEARCH = 2, 52 | ST_MULTIFRAME_VALIDATE = 3, 53 | ST_ALIGNED = 4; 54 | 55 | reg [2:0] fsm_state; 56 | reg [2:0] fsm_state_nxt; 57 | 58 | 59 | // Signals 60 | // ------- 61 | 62 | // Input 63 | reg strobe; 64 | reg [7:0] data; 65 | reg data_match_fas; 66 | 67 | // Position tracking 68 | reg [2:0] bit; 69 | reg bit_first; 70 | reg bit_last; 71 | 72 | reg [4:0] ts; 73 | reg ts_is_ts0; 74 | reg ts_is_ts31; 75 | 76 | reg [3:0] frame; 77 | reg frame_smf_first; // First of a sub-multiframe 78 | reg frame_smf_last; // Last of a sub-multiframe 79 | reg frame_mf_first; // First of the multiframe 80 | reg frame_mf_last; // Last of the multiframe 81 | 82 | // Alignement control signal 83 | wire align_frame; 84 | wire align_mframe; 85 | 86 | // Helpers 87 | reg fas_pos; 88 | reg [6:0] mfa_timeout; 89 | 90 | reg [15:0] ts0_msbs; 91 | wire [5:0] ts0_msbs_sync; 92 | wire [3:0] ts0_msbs_crc; 93 | reg ts0_msbs_match_mf; 94 | reg ts0_msbs_match_crc; 95 | 96 | wire crc_in_bit; 97 | wire crc_in_first; 98 | wire crc_in_valid; 99 | wire crc_capture; 100 | 101 | wire [3:0] crc_out; 102 | reg [3:0] crc_smf; 103 | 104 | reg ed_fas, ep_fas; 105 | reg ed_nfas, ep_nfas; 106 | reg ed_crc, ep_crc; 107 | reg ed_mfa, ep_mfa; 108 | 109 | reg [1:0] ec_fas; 110 | reg [1:0] ec_nfas; 111 | reg [1:0] ec_crc; 112 | reg [1:0] ec_mfa; 113 | 114 | reg error; 115 | 116 | 117 | // Input shift register 118 | // -------------------- 119 | 120 | // Strobe signal 121 | always @(posedge clk) 122 | if (rst) 123 | strobe <= 1'b0; 124 | else 125 | strobe <= in_valid; 126 | 127 | // Actual data 128 | always @(posedge clk) 129 | if (in_valid) 130 | data <= { data[6:0], in_bit }; 131 | 132 | // Pre-matching of FAS 133 | always @(posedge clk) 134 | if (in_valid) 135 | data_match_fas <= (data[5:0] == 6'b001101) & in_bit; 136 | 137 | 138 | // FSM logic 139 | // --------- 140 | 141 | // State register 142 | always @(posedge clk) 143 | if (rst | error) 144 | fsm_state <= ST_FRAME_SEARCH; 145 | else if (strobe) 146 | fsm_state <= fsm_state_nxt; 147 | 148 | // State transitions 149 | always @(*) 150 | begin 151 | // Default is to stay on the current state 152 | fsm_state_nxt <= fsm_state; 153 | 154 | // Act depending on current state 155 | case (fsm_state) 156 | ST_FRAME_SEARCH: begin 157 | // As soon as we have a FAS, we assume we're byte align 158 | // and check it's the right one 159 | if (data_match_fas) 160 | fsm_state_nxt <= ST_FRAME_VALIDATE; 161 | end 162 | 163 | ST_FRAME_VALIDATE: begin 164 | // We expect a non-FAS then a FAS, any error and we retry 165 | // frame search 166 | if (bit_last & ts_is_ts0) 167 | if (fas_pos) 168 | fsm_state_nxt <= data_match_fas ? ( 169 | ctrl_mode_mf ? ST_MULTIFRAME_SEARCH : ST_ALIGNED 170 | ) : ST_FRAME_SEARCH; 171 | else 172 | fsm_state_nxt <= data[6] ? ST_FRAME_VALIDATE : ST_FRAME_SEARCH; 173 | end 174 | 175 | ST_MULTIFRAME_SEARCH: begin 176 | // Either we find a possible alignement and we proceed to 177 | // validate it, or we timeout and fall back to frame search 178 | if (bit_last & ts_is_ts0) 179 | if (mfa_timeout[6]) 180 | fsm_state_nxt <= ST_FRAME_SEARCH; 181 | else if (ts0_msbs_match_mf) 182 | fsm_state_nxt <= ST_MULTIFRAME_VALIDATE; 183 | end 184 | 185 | ST_MULTIFRAME_VALIDATE: begin 186 | // If we get a second alignement of the MSBs at the right 187 | // position before the timeout, we're good and aligned ! 188 | if (bit_last & ts_is_ts0) 189 | if (mfa_timeout[6]) 190 | fsm_state_nxt <= ST_FRAME_SEARCH; 191 | else if (frame_mf_first & ts0_msbs_match_mf) 192 | fsm_state_nxt <= ST_ALIGNED; 193 | end 194 | 195 | ST_ALIGNED: begin 196 | // Nothing to do. Only error case cas get us out and they're 197 | // handled separately 198 | end 199 | endcase 200 | end 201 | 202 | 203 | // Position tracking 204 | // ----------------- 205 | 206 | // Bit position 207 | always @(posedge clk) 208 | if (align_frame) begin 209 | bit <= 3'b000; 210 | bit_first <= 1'b1; 211 | bit_last <= 1'b0; 212 | end else if (strobe) begin 213 | bit <= bit + 1; 214 | bit_first <= (bit == 3'b111); 215 | bit_last <= (bit == 3'b110); 216 | end 217 | 218 | // Time Slot 219 | always @(posedge clk) 220 | if (align_frame) begin 221 | ts <= 5'h01; 222 | ts_is_ts0 <= 1'b0; 223 | ts_is_ts31 <= 1'b0; 224 | end else if (strobe & bit_last) begin 225 | ts <= ts + 1; 226 | ts_is_ts0 <= ts_is_ts31; 227 | ts_is_ts31 <= (ts == 5'h1e); 228 | end 229 | 230 | // Frame 231 | always @(posedge clk) 232 | if (align_mframe) begin 233 | frame <= 4'h0; 234 | frame_smf_first <= 1'b1; 235 | frame_smf_last <= 1'b0; 236 | frame_mf_first <= 1'b1; 237 | frame_mf_last <= 1'b0; 238 | end else if (strobe & bit_last & ts_is_ts31) begin 239 | frame <= frame + 1; 240 | frame_smf_first <= frame_smf_last; 241 | frame_smf_last <= (frame[2:0] == 3'h6); 242 | frame_mf_first <= frame_mf_last; 243 | frame_mf_last <= (frame == 4'he); 244 | end 245 | 246 | // Control for alignement 247 | assign align_frame = (fsm_state == ST_FRAME_SEARCH); 248 | assign align_mframe = (fsm_state == ST_FRAME_VALIDATE) || (fsm_state == ST_MULTIFRAME_SEARCH); 249 | 250 | 251 | // Helpers 252 | // ------- 253 | 254 | // Frame Alignement Signal position tracking 255 | // During ST_FRAME_SEARCH, the frame counter is still locked until we 256 | // have multi-frame alignement. So just track the LSB of the frame 257 | // number independently so we can check the next FAS 258 | always @(posedge clk) 259 | if (align_frame) 260 | fas_pos <= 1'b0; 261 | else 262 | fas_pos <= fas_pos ^ (strobe & bit_last & ts_is_ts0); 263 | 264 | // Multi Frame Alignement timout 265 | // We have 8 ms = 64 frames to acquire multi frame alignement 266 | always @(posedge clk) 267 | if (align_frame) 268 | mfa_timeout <= 7'h3f; 269 | else if (strobe & bit_last & ts_is_ts0) 270 | mfa_timeout <= mfa_timeout - 1; 271 | 272 | // Track the history of all 16 TS0 MSBs 273 | // and also update some pre-matching flags 274 | always @(posedge clk) 275 | if (fsm_state == ST_FRAME_SEARCH) begin 276 | // If we're not aligned =>avoid spurious matches 277 | ts0_msbs <= 16'hffff; 278 | ts0_msbs_match_mf <= 1'b0; 279 | ts0_msbs_match_crc <= 1'b0; 280 | end else if (strobe & ts_is_ts0 & bit_first) begin 281 | // We register it ASAP so that when we have the full byte (i.e. 282 | // when the FSM updates), the history is up to date 283 | ts0_msbs <= { ts0_msbs[14:0], data[0] }; 284 | ts0_msbs_match_mf <= (ts0_msbs_sync == 6'b001011); 285 | ts0_msbs_match_crc <= (crc_smf == ts0_msbs_crc); 286 | end 287 | 288 | assign ts0_msbs_sync = { ts0_msbs[14], ts0_msbs[12], ts0_msbs[10], ts0_msbs[8], ts0_msbs[6], ts0_msbs[4] }; 289 | assign ts0_msbs_crc = { ts0_msbs[6], ts0_msbs[4], ts0_msbs[2], ts0_msbs[0] }; 290 | 291 | // CRC4 computation 292 | assign crc_in_bit = (bit_first & ts_is_ts0 & fas_pos) ? 1'b0 : data[0]; 293 | assign crc_in_first = bit_first & ts_is_ts0 & frame_smf_first; 294 | assign crc_in_valid = strobe; 295 | assign crc_capture = crc_in_first; 296 | 297 | e1_crc4 crc_I ( 298 | .in_bit(crc_in_bit), 299 | .in_first(crc_in_first), 300 | .in_valid(crc_in_valid), 301 | .out_crc4(crc_out), 302 | .clk(clk), 303 | .rst(rst) 304 | ); 305 | 306 | always @(posedge clk) 307 | if (crc_capture) 308 | crc_smf <= crc_out; 309 | 310 | // Track errors of FAS, non-FAS, CRC 311 | // We register these detection bits and so the counter will be 'late' 312 | // but they're used as LOS detection which is pretty much async to the 313 | // rest and used to go back to ST_SEARCH_BIT anyway ... 314 | 315 | always @(posedge clk) 316 | // Only track when we're frame aligned 317 | if ((fsm_state != ST_MULTIFRAME_SEARCH) && (fsm_state != ST_ALIGNED)) begin 318 | ep_fas <= 1'b0; 319 | ed_fas <= 1'b0; 320 | ep_nfas <= 1'b0; 321 | ed_nfas <= 1'b0; 322 | end else begin 323 | ep_fas <= strobe & bit_last & ts_is_ts0 & fas_pos; 324 | ed_fas <= strobe & bit_last & ts_is_ts0 & fas_pos & ~data_match_fas; 325 | ep_nfas <= strobe & bit_last & ts_is_ts0 & ~fas_pos; 326 | ed_nfas <= strobe & bit_last & ts_is_ts0 & ~fas_pos & ~data[6]; 327 | end 328 | 329 | always @(posedge clk) 330 | // CRC and MultiFrameAlign errors tracked only when properly 331 | // aligned to the multiframe 332 | if ((fsm_state != ST_ALIGNED) | ~ctrl_mode_mf) begin 333 | ep_crc <= 1'b0; 334 | ed_crc <= 1'b0; 335 | ep_mfa <= 1'b0; 336 | ed_mfa <= 1'b0; 337 | end else begin 338 | ep_crc <= strobe & bit_last & ts_is_ts0 & frame_smf_last; 339 | ed_crc <= strobe & bit_last & ts_is_ts0 & frame_smf_last & ~ts0_msbs_match_crc; 340 | ep_mfa <= strobe & bit_last & ts_is_ts0 & frame_mf_first; 341 | ed_mfa <= strobe & bit_last & ts_is_ts0 & frame_mf_first & ~ts0_msbs_match_mf; 342 | end 343 | 344 | always @(posedge clk) 345 | if (fsm_state == ST_FRAME_SEARCH) begin 346 | ec_fas <= 0; 347 | ec_nfas <= 0; 348 | ec_crc <= 0; 349 | ec_mfa <= 0; 350 | end else begin 351 | ec_fas <= (ep_fas & ~ed_fas) ? 0 : (ec_fas + ed_fas); 352 | ec_nfas <= (ep_nfas & ~ed_nfas) ? 0 : (ec_nfas + ed_nfas); 353 | ec_crc <= (ep_crc & ~ed_crc) ? 0 : (ec_crc + ed_crc); 354 | ec_mfa <= (ep_mfa & ~ed_mfa) ? 0 : (ec_mfa + ed_mfa); 355 | end 356 | 357 | always @(posedge clk) 358 | error <= (ec_fas == 2'b11) | (ec_nfas == 2'b11) | (ec_crc == 2'b11) | (ec_mfa == 2'b11); 359 | 360 | 361 | // Output 362 | // ------ 363 | 364 | // Data output 365 | always @(posedge clk) 366 | if (rst) begin 367 | out_valid <= 1'b0; 368 | out_data <= 8'h00; 369 | out_frame <= 4'h0; 370 | out_ts <= 5'h00; 371 | out_ts_is0 <= 1'b1; 372 | out_first <= 1'b1; 373 | out_last <= 1'b0; 374 | end else begin 375 | if (TS0_START) 376 | out_valid <= strobe && bit_last && ( 377 | (fsm_state == ST_ALIGNED) || 378 | ( 379 | (fsm_state == ST_MULTIFRAME_VALIDATE) && 380 | (ts_is_ts0 && ~mfa_timeout[6] && frame_mf_first && ts0_msbs_match_mf) 381 | ) || 382 | ( 383 | ~ctrl_mode_mf && 384 | (fsm_state == ST_FRAME_VALIDATE) && 385 | (ts_is_ts0 && fas_pos && data_match_fas) 386 | ) 387 | ); 388 | else 389 | out_valid <= strobe & bit_last & (fsm_state == ST_ALIGNED); 390 | 391 | out_data <= data; 392 | out_frame <= frame; 393 | out_ts <= ts; 394 | out_ts_is0 <= ts_is_ts0; 395 | out_first <= ts_is_ts0 & frame_mf_first; 396 | out_last <= ts_is_ts31 & frame_mf_last; 397 | end 398 | 399 | // Error indicators 400 | assign out_err_crc = ed_crc; 401 | assign out_err_mfa = ed_mfa; 402 | assign out_err_fas = ed_fas; 403 | assign out_err_nfas = ed_nfas; 404 | 405 | // Status 406 | always @(posedge clk) 407 | if (rst) 408 | aligned <= 1'b0; 409 | else 410 | aligned <= fsm_state == ST_ALIGNED; 411 | 412 | endmodule // e1_rx_deframer 413 | -------------------------------------------------------------------------------- /rtl/e1_rx_filter.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_rx_filter.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 RX glitch filtering and pulse detection 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_rx_filter ( 15 | // Input 16 | input wire in_hi, 17 | input wire in_lo, 18 | 19 | // Output 20 | output reg out_hi, 21 | output reg out_lo, 22 | output reg out_stb, // Strobe on any 0->1 transition 23 | 24 | // Common 25 | input wire clk, 26 | input wire rst 27 | ); 28 | 29 | // Signals 30 | reg in_hi_r; 31 | reg in_lo_r; 32 | 33 | reg [1:0] cnt_hi; 34 | reg [1:0] cnt_lo; 35 | 36 | // Register incoming data first 37 | // They come from IO register buffer, but from async data, so a 38 | // second FF is good practice 39 | always @(posedge clk) 40 | begin 41 | in_hi_r <= in_hi; 42 | in_lo_r <= in_lo; 43 | end 44 | 45 | // Counters 46 | always @(posedge clk) 47 | begin 48 | if (rst) begin 49 | cnt_hi <= 2'b00; 50 | cnt_lo <= 2'b00; 51 | end else begin 52 | // Hi 53 | if (in_hi_r & ~in_lo_r & (cnt_hi != 2'b11)) 54 | cnt_hi <= cnt_hi + 1; 55 | else if (~in_hi_r & cnt_hi != 2'b00) 56 | cnt_hi <= cnt_hi - 1; 57 | else 58 | cnt_hi <= cnt_hi; 59 | 60 | // Lo 61 | if (in_lo_r & ~in_hi_r & (cnt_lo != 2'b11)) 62 | cnt_lo <= cnt_lo + 1; 63 | else if (~in_lo_r & (cnt_lo != 2'b00)) 64 | cnt_lo <= cnt_lo - 1; 65 | else 66 | cnt_lo <= cnt_lo; 67 | end 68 | end 69 | 70 | // Flip flops 71 | always @(posedge clk) 72 | begin 73 | // Default is no 1->0 transition 74 | out_stb <= 1'b0; 75 | 76 | // Hi 77 | if (cnt_hi == 2'b11 & ~out_hi & ~out_lo) begin 78 | out_hi <= 1'b1; 79 | out_stb <= 1'b1; 80 | end else if (cnt_hi == 2'b00) 81 | out_hi <= 1'b0; 82 | 83 | // Lo 84 | if (cnt_lo == 2'b11 & ~out_lo & ~out_hi) begin 85 | out_lo <= 1'b1; 86 | out_stb <= 1'b1; 87 | end else if (cnt_lo == 2'b00) 88 | out_lo <= 1'b0; 89 | end 90 | 91 | endmodule // e1_rx_filter 92 | -------------------------------------------------------------------------------- /rtl/e1_rx_liu.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_rx_liu.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 RX interface to external LIU 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_rx_liu ( 15 | // Pads 16 | input wire pad_rx_data, 17 | input wire pad_rx_clk, 18 | 19 | // Output 20 | output reg out_data, 21 | output reg out_valid, 22 | 23 | // Common 24 | input wire clk, 25 | input wire rst 26 | ); 27 | 28 | wire rx_data; 29 | wire rx_clk; 30 | 31 | reg rx_data_r; 32 | reg rx_clk_r; 33 | 34 | // IOBs (registered) 35 | SB_IO #( 36 | .PIN_TYPE(6'b0000_00), 37 | .PULLUP(1'b0), 38 | .NEG_TRIGGER(1'b0) 39 | ) rx_iobs_I[1:0] ( 40 | .PACKAGE_PIN({pad_rx_data, pad_rx_clk}), 41 | .CLOCK_ENABLE(1'b1), 42 | .INPUT_CLK(clk), 43 | .OUTPUT_CLK(1'b0), 44 | .OUTPUT_ENABLE(1'b0), 45 | .D_OUT_0(1'b0), 46 | .D_OUT_1(1'b0), 47 | .D_IN_0({rx_data, rx_clk}), 48 | .D_IN_1() 49 | ); 50 | 51 | // First internal register 52 | always @(posedge clk) 53 | begin 54 | rx_data_r <= rx_data; 55 | rx_clk_r <= rx_clk; 56 | end 57 | 58 | // Second internal register + clk falling edge detect 59 | always @(posedge clk) 60 | begin 61 | out_data <= rx_data_r; 62 | out_valid <= rx_clk_r & ~rx_clk; 63 | end 64 | 65 | endmodule // e1_rx_liu 66 | -------------------------------------------------------------------------------- /rtl/e1_rx_phy.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_rx_phy.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 RX IOB instances 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_rx_phy ( 15 | // IO pads 16 | input wire pad_rx_hi_p, 17 | input wire pad_rx_hi_n, // Unused in ice40 18 | input wire pad_rx_lo_p, 19 | input wire pad_rx_lo_n, // Unused in ice40 20 | 21 | // Output 22 | output wire rx_hi, 23 | output wire rx_lo, 24 | 25 | // Common 26 | input wire clk, 27 | input wire rst 28 | ); 29 | 30 | SB_IO #( 31 | .PIN_TYPE(6'b000000), 32 | .PULLUP(1'b0), 33 | .NEG_TRIGGER(1'b0), 34 | .IO_STANDARD("SB_LVDS_INPUT") 35 | ) rx_hi_I ( 36 | .PACKAGE_PIN(pad_rx_hi_p), 37 | .CLOCK_ENABLE(1'b1), 38 | .INPUT_CLK(clk), 39 | .OUTPUT_CLK(1'b0), 40 | .OUTPUT_ENABLE(1'b0), 41 | .D_OUT_0(1'b0), 42 | .D_OUT_1(1'b0), 43 | .D_IN_0(rx_hi), 44 | .D_IN_1() 45 | ); 46 | 47 | SB_IO #( 48 | .PIN_TYPE(6'b000000), 49 | .PULLUP(1'b0), 50 | .NEG_TRIGGER(1'b0), 51 | .IO_STANDARD("SB_LVDS_INPUT") 52 | ) rx_lo_I ( 53 | .PACKAGE_PIN(pad_rx_lo_p), 54 | .CLOCK_ENABLE(1'b1), 55 | .INPUT_CLK(clk), 56 | .OUTPUT_CLK(1'b0), 57 | .OUTPUT_ENABLE(1'b0), 58 | .D_OUT_0(1'b0), 59 | .D_OUT_1(1'b0), 60 | .D_IN_0(rx_lo), 61 | .D_IN_1() 62 | ); 63 | 64 | endmodule // e1_rx_phy 65 | -------------------------------------------------------------------------------- /rtl/e1_tx.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_tx.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 TX top-level 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_tx #( 15 | parameter integer LIU = 0, 16 | parameter integer MFW = 7 17 | )( 18 | // IO pads 19 | // Raw PHY 20 | output wire pad_tx_hi, 21 | output wire pad_tx_lo, 22 | 23 | // LIU 24 | output wire pad_tx_data, 25 | output wire pad_tx_clk, 26 | 27 | // Buffer interface 28 | input wire [7:0] buf_data, 29 | output wire [4:0] buf_ts, 30 | output wire [3:0] buf_frame, 31 | output wire [MFW-1:0] buf_mf, 32 | output wire buf_re, 33 | input wire buf_rdy, 34 | 35 | // BD interface 36 | input wire [MFW-1:0] bd_mf, 37 | input wire [1:0] bd_crc_e, 38 | input wire bd_valid, 39 | output reg bd_done, 40 | output reg bd_miss, 41 | 42 | // Loopback input 43 | input wire lb_bit, 44 | input wire lb_valid, 45 | 46 | // Control 47 | input wire ctrl_time_src, // 0=internal, 1=external 48 | input wire ctrl_do_framing, 49 | input wire ctrl_do_crc4, 50 | input wire ctrl_loopback, 51 | input wire alarm, 52 | 53 | // Timing sources 54 | input wire ext_tick, 55 | output wire int_tick, 56 | 57 | // Common 58 | input wire clk, 59 | input wire rst 60 | ); 61 | 62 | // Signals 63 | // ------- 64 | 65 | // Buffer Descriptor handling 66 | reg mf_valid; 67 | 68 | // Framer 69 | wire [7:0] f_data; 70 | wire [1:0] f_crc_e; 71 | wire [3:0] f_frame; 72 | wire [4:0] f_ts; 73 | wire f_mf_first; 74 | wire f_mf_last; 75 | wire f_req; 76 | wire f_rdy; 77 | 78 | // Low-level (bit -> pulses) 79 | wire ll_bit, ll_valid; 80 | wire ll_pg_hi, ll_pg_lo, ll_pg_stb; 81 | wire ll_raw_hi, ll_raw_lo; 82 | 83 | // Pulse generator 84 | reg [4:0] pg_hi; 85 | reg [4:0] pg_lo; 86 | 87 | 88 | // Frame generation 89 | // ---------------- 90 | 91 | // Buffer Descriptor 92 | // Keep track if we're in a valid MF at all 93 | always @(posedge clk or posedge rst) 94 | if (rst) 95 | mf_valid <= 1'b0; 96 | else if (f_req & f_mf_first) 97 | mf_valid <= bd_valid; 98 | 99 | // We register those because a 1 cycle delay doesn't matter 100 | always @(posedge clk) 101 | begin 102 | bd_done <= f_req & f_mf_last & mf_valid; 103 | bd_miss <= f_req & f_mf_first & ~bd_valid; 104 | end 105 | 106 | // Buffer read 107 | assign buf_ts = f_ts; 108 | assign buf_frame = f_frame; 109 | assign buf_mf = bd_mf; 110 | assign buf_re = f_req & bd_valid; 111 | 112 | assign f_data = buf_data; 113 | assign f_crc_e = bd_crc_e; 114 | assign f_rdy = buf_rdy & mf_valid; 115 | 116 | // Framer 117 | e1_tx_framer framer_I ( 118 | .in_data(f_data), 119 | .in_crc_e(f_crc_e), 120 | .in_frame(f_frame), 121 | .in_ts(f_ts), 122 | .in_mf_first(f_mf_first), 123 | .in_mf_last(f_mf_last), 124 | .in_req(f_req), 125 | .in_rdy(f_rdy), 126 | .out_bit(ll_bit), 127 | .out_valid(ll_valid), 128 | .lb_bit(lb_bit), 129 | .lb_valid(lb_valid), 130 | .ctrl_time_src(ctrl_time_src), 131 | .ctrl_do_framing(ctrl_do_framing), 132 | .ctrl_do_crc4(ctrl_do_crc4), 133 | .ctrl_loopback(ctrl_loopback), 134 | .alarm(alarm), 135 | .ext_tick(ext_tick), 136 | .int_tick(int_tick), 137 | .clk(clk), 138 | .rst(rst) 139 | ); 140 | 141 | 142 | // Low-level 143 | // --------- 144 | 145 | generate 146 | if (LIU == 0) begin 147 | 148 | // HDB3 encoding 149 | hdb3_enc hdb3_I ( 150 | .out_pos(ll_pg_hi), 151 | .out_neg(ll_pg_lo), 152 | .out_valid(ll_pg_stb), 153 | .in_data(ll_bit), 154 | .in_valid(ll_valid), 155 | .clk(clk), 156 | .rst(rst) 157 | ); 158 | 159 | // Pulse generation 160 | always @(posedge clk) 161 | begin 162 | if (rst) begin 163 | pg_hi <= 0; 164 | pg_lo <= 0; 165 | end else begin 166 | if (ll_pg_stb) begin 167 | pg_hi <= ll_pg_hi ? 5'h19 : 5'h00; 168 | pg_lo <= ll_pg_lo ? 5'h19 : 5'h00; 169 | end else begin 170 | pg_hi <= pg_hi - pg_hi[4]; 171 | pg_lo <= pg_lo - pg_lo[4]; 172 | end 173 | end 174 | end 175 | 176 | assign ll_raw_hi = pg_hi[4]; 177 | assign ll_raw_lo = pg_lo[4]; 178 | 179 | // PHY 180 | e1_tx_phy phy_I ( 181 | .pad_tx_hi(pad_tx_hi), 182 | .pad_tx_lo(pad_tx_lo), 183 | .tx_hi(ll_raw_hi), 184 | .tx_lo(ll_raw_lo), 185 | .clk(clk), 186 | .rst(rst) 187 | ); 188 | 189 | end else begin 190 | 191 | // LIU interface 192 | e1_tx_liu liuif_I ( 193 | .pad_tx_data(pad_tx_data), 194 | .pad_tx_clk(pad_tx_clk), 195 | .in_data(ll_bit), 196 | .in_valid(ll_valid), 197 | .clk(clk), 198 | .rst(rst) 199 | ); 200 | 201 | end 202 | endgenerate 203 | 204 | endmodule // e1_tx 205 | -------------------------------------------------------------------------------- /rtl/e1_tx_framer.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_tx_framer.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 Frame generation as described G.704 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_tx_framer ( 15 | // Fetch interface 16 | input wire [7:0] in_data, 17 | input wire [1:0] in_crc_e, // CRC error bits to use in this multiframe 18 | output wire [3:0] in_frame, 19 | output wire [4:0] in_ts, 20 | output reg in_mf_first, // First request for this multiframe 21 | output reg in_mf_last, // Last request for this multiframe 22 | output reg in_req, 23 | input wire in_rdy, 24 | 25 | // Output 26 | output reg out_bit, 27 | output reg out_valid, 28 | 29 | // Loopback Input 30 | input wire lb_bit, 31 | input wire lb_valid, 32 | 33 | // Control 34 | input wire ctrl_time_src, // 0=internal, 1=external 35 | input wire ctrl_do_framing, 36 | input wire ctrl_do_crc4, 37 | input wire ctrl_loopback, 38 | input wire alarm, 39 | 40 | // Timing sources 41 | input wire ext_tick, 42 | output wire int_tick, 43 | 44 | // Common 45 | input wire clk, 46 | input wire rst 47 | ); 48 | 49 | // Signals 50 | // ------- 51 | 52 | // Tick source 53 | reg [4:0] tick_cnt; 54 | reg strobe; 55 | 56 | // Fetch unit 57 | reg [3:0] fetch_frame; 58 | reg [4:0] fetch_ts; 59 | reg fetch_ts_is0; 60 | reg fetch_ts_is31; 61 | reg fetch_done; 62 | 63 | wire [7:0] fetch_data; 64 | wire [1:0] fetch_crc_e; 65 | 66 | wire fetch_ack; 67 | 68 | // TS0 generation 69 | reg [7:0] shift_data_nxt; 70 | wire [7:0] odd_bit0; 71 | wire odd_bit0_mux; 72 | 73 | // Shift register 74 | reg [3:0] bit_cnt; 75 | reg bit_first; 76 | 77 | reg [7:0] shift_data; 78 | reg shift_at_first; 79 | reg shift_at_last; 80 | reg shift_at_crc; 81 | 82 | // CRC4 83 | wire crc_in_bit; 84 | wire crc_in_first; 85 | wire crc_in_valid; 86 | reg crc_capture; 87 | 88 | wire [3:0] crc_out; 89 | reg [3:0] crc_smf; 90 | 91 | 92 | // Tick source 93 | // ----------- 94 | 95 | always @(posedge clk or posedge rst) 96 | if (rst) 97 | tick_cnt <= 5'b00000; 98 | else 99 | tick_cnt <= strobe ? 5'b01100 : (tick_cnt - 1); 100 | 101 | always @(posedge clk) 102 | strobe <= (ctrl_time_src ? ext_tick : tick_cnt[4]) & ~strobe; 103 | 104 | assign int_tick = strobe; 105 | 106 | 107 | // Fetch control 108 | // ------------- 109 | 110 | // Frame 111 | always @(posedge clk or posedge rst) 112 | if (rst) 113 | fetch_frame <= 4'hf; 114 | else if (fetch_ack) 115 | fetch_frame <= fetch_frame + fetch_ts_is31; 116 | 117 | // Time Slot 118 | always @(posedge clk or posedge rst) 119 | if (rst) begin 120 | fetch_ts <= 5'h1f; 121 | fetch_ts_is0 <= 1'b0; 122 | fetch_ts_is31 <= 1'b1; 123 | end else if (fetch_ack) begin 124 | fetch_ts <= fetch_ts + 1; 125 | fetch_ts_is0 <= fetch_ts_is31; 126 | fetch_ts_is31 <= (fetch_ts == 5'h1e); 127 | end 128 | 129 | // External request 130 | assign in_frame = fetch_frame; 131 | assign in_ts = fetch_ts; 132 | 133 | always @(posedge clk or posedge rst) 134 | if (rst) begin 135 | in_mf_first <= 1'b0; 136 | in_mf_last <= 1'b1; 137 | end else if (fetch_ack) begin 138 | in_mf_first <= in_mf_last; 139 | in_mf_last <= (fetch_frame == 4'hf) && (fetch_ts == 5'h1e) ; 140 | end 141 | 142 | always @(posedge clk) 143 | in_req <= fetch_ack; 144 | 145 | // Track the first ever request (hence first valid data ...) 146 | always @(posedge clk or posedge rst) 147 | if (rst) 148 | fetch_done <= 1'b0; 149 | else if (in_req) 150 | fetch_done <= 1'b1; 151 | 152 | // Data output to next stage 153 | assign fetch_data = (in_rdy & fetch_done) ? in_data : 8'hff; 154 | assign fetch_crc_e = in_crc_e; 155 | 156 | 157 | // TS0 generation 158 | // -------------- 159 | // After fetch_ack we have plenty of time to generate the next data 160 | // from the response 161 | 162 | assign odd_bit0 = { fetch_crc_e[1:0], 6'b110100 }; 163 | assign odd_bit0_mux = odd_bit0[fetch_frame[3:1]]; 164 | 165 | always @(posedge clk) 166 | if (fetch_ts_is0 & ctrl_do_framing) begin 167 | // TS0 with auto-framing 168 | if (fetch_frame[0]) 169 | // Odd frame number 170 | shift_data_nxt <= { 171 | ctrl_do_crc4 ? odd_bit0_mux : fetch_data[7], 172 | 1'b1, 173 | alarm, 174 | fetch_data[4:0] 175 | }; 176 | else 177 | // Even frame number 178 | shift_data_nxt <= { fetch_data[7], 7'h1b }; // CRC bits are set later 179 | end else begin 180 | // Either auto-frame is disabled, or this is not TS0 181 | // If there is no valid data available, fill with 0xff 182 | shift_data_nxt <= fetch_data; 183 | end 184 | 185 | 186 | // Shift register 187 | // -------------- 188 | 189 | // Bit counter 190 | always @(posedge clk or posedge rst) 191 | if (rst) 192 | bit_cnt <= 4'b1000; 193 | else if (strobe) 194 | bit_cnt <= bit_cnt[3] ? 4'b0110 : (bit_cnt - 1); 195 | 196 | // Shift register 197 | always @(posedge clk or posedge rst) 198 | if (rst) 199 | shift_data <= 8'hff; 200 | else if (strobe) 201 | shift_data <= bit_cnt[3] ? shift_data_nxt : { shift_data[6:0], 1'b1 }; 202 | 203 | // Ack to upstream 204 | assign fetch_ack = strobe & bit_cnt[3]; 205 | 206 | // Track special positions 207 | always @(posedge clk or posedge rst) 208 | if (rst) begin 209 | shift_at_first <= 1'b1; 210 | shift_at_last <= 1'b0; 211 | shift_at_crc <= 1'b0; 212 | end else if (strobe) begin 213 | shift_at_first <= (fetch_frame[2:0] == 3'b000) & fetch_ts_is0 & bit_cnt[3]; 214 | shift_at_last <= (fetch_frame[2:0] == 3'b000) & fetch_ts_is0 & (bit_cnt[2:0] == 3'b000); 215 | shift_at_crc <= ~fetch_frame[0] & fetch_ts_is0 & bit_cnt[3]; 216 | end 217 | 218 | 219 | // CRC4 220 | // ---- 221 | 222 | // CRC4 computation 223 | assign crc_in_bit = shift_at_crc ? 1'b0 : shift_data[7]; 224 | assign crc_in_first = shift_at_first; 225 | assign crc_in_valid = strobe; 226 | 227 | always @(posedge clk) 228 | crc_capture <= shift_at_last; 229 | 230 | e1_crc4 crc_I ( 231 | .in_bit(crc_in_bit), 232 | .in_first(crc_in_first), 233 | .in_valid(crc_in_valid), 234 | .out_crc4(crc_out), 235 | .clk(clk), 236 | .rst(rst) 237 | ); 238 | 239 | always @(posedge clk or posedge rst) 240 | if (rst) 241 | crc_smf <= 4'b1111; 242 | else if (crc_capture) 243 | crc_smf <= crc_out; 244 | else if (shift_at_crc & strobe) 245 | crc_smf <= { crc_smf[2:0], 1'b1 }; 246 | 247 | 248 | // Output 249 | // ------ 250 | 251 | always @(posedge clk or posedge rst) 252 | if (rst) begin 253 | out_bit <= 1'b1; 254 | out_valid <= 1'b0; 255 | end else begin 256 | out_bit <= ctrl_loopback ? lb_bit : ((ctrl_do_crc4 & shift_at_crc) ? crc_smf[3] : shift_data[7]); 257 | out_valid <= ctrl_loopback ? lb_valid : strobe; 258 | end 259 | 260 | endmodule // e1_tx_framer 261 | -------------------------------------------------------------------------------- /rtl/e1_tx_liu.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_tx_liu.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 RX interface to external LIU 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_tx_liu ( 15 | // Pads 16 | input wire pad_tx_data, 17 | input wire pad_tx_clk, 18 | 19 | // Intput 20 | input wire in_data, 21 | input wire in_valid, 22 | 23 | // Common 24 | input wire clk, 25 | input wire rst 26 | ); 27 | // Signals 28 | reg [5:0] cnt_cur; 29 | reg [5:0] cnt_nxt; 30 | 31 | reg tx_data; 32 | wire tx_clk; 33 | 34 | // Counters 35 | always @(posedge clk) 36 | if (in_valid) 37 | cnt_nxt <= 0; 38 | else 39 | cnt_nxt <= cnt_nxt + 1; 40 | 41 | always @(posedge clk) 42 | if (in_valid) 43 | cnt_cur <= { 1'b1, cnt_nxt[5:1] }; 44 | else 45 | cnt_cur <= cnt_cur - 1; 46 | 47 | // TX 48 | always @(posedge clk) 49 | if (in_valid) 50 | tx_data <= in_data; 51 | 52 | assign tx_clk = cnt_cur[5]; 53 | 54 | // IOBs (registered) 55 | SB_IO #( 56 | .PIN_TYPE(6'b0101_00), 57 | .PULLUP(1'b0), 58 | .NEG_TRIGGER(1'b0) 59 | ) tx_data_iob_I ( 60 | .PACKAGE_PIN(pad_tx_data), 61 | .CLOCK_ENABLE(1'b1), 62 | .INPUT_CLK(1'b0), 63 | .OUTPUT_CLK(clk), 64 | .OUTPUT_ENABLE(1'b1), 65 | .D_OUT_0(tx_data), 66 | .D_OUT_1(1'b0), 67 | .D_IN_0(), 68 | .D_IN_1() 69 | ); 70 | 71 | SB_IO #( 72 | .PIN_TYPE(6'b0101_00), 73 | .PULLUP(1'b0), 74 | .NEG_TRIGGER(1'b0) 75 | ) tx_clk_iob_I ( 76 | .PACKAGE_PIN(pad_tx_clk), 77 | .CLOCK_ENABLE(1'b1), 78 | .INPUT_CLK(1'b0), 79 | .OUTPUT_CLK(clk), 80 | .OUTPUT_ENABLE(1'b1), 81 | .D_OUT_0(tx_clk), 82 | .D_OUT_1(1'b0), 83 | .D_IN_0(), 84 | .D_IN_1() 85 | ); 86 | 87 | endmodule // e1_tx_liu 88 | -------------------------------------------------------------------------------- /rtl/e1_tx_phy.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_tx_phy.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 TX IOB instances 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_tx_phy ( 15 | // IO pads 16 | output wire pad_tx_hi, 17 | output wire pad_tx_lo, 18 | 19 | // Input 20 | input wire tx_hi, 21 | input wire tx_lo, 22 | 23 | // Common 24 | input wire clk, 25 | input wire rst 26 | ); 27 | 28 | SB_IO #( 29 | .PIN_TYPE(6'b010100), 30 | .PULLUP(1'b0), 31 | .NEG_TRIGGER(1'b0), 32 | .IO_STANDARD("SB_LVCMOS") 33 | ) tx_hi_I ( 34 | .PACKAGE_PIN(pad_tx_hi), 35 | .CLOCK_ENABLE(1'b1), 36 | .INPUT_CLK(1'b0), 37 | .OUTPUT_CLK(clk), 38 | .OUTPUT_ENABLE(1'b0), 39 | .D_OUT_0(tx_hi), 40 | .D_OUT_1(1'b0), 41 | .D_IN_0(), 42 | .D_IN_1() 43 | ); 44 | 45 | SB_IO #( 46 | .PIN_TYPE(6'b010100), 47 | .PULLUP(1'b0), 48 | .NEG_TRIGGER(1'b0), 49 | .IO_STANDARD("SB_LVCMOS") 50 | ) tx_lo_I ( 51 | .PACKAGE_PIN(pad_tx_lo), 52 | .CLOCK_ENABLE(1'b1), 53 | .INPUT_CLK(1'b0), 54 | .OUTPUT_CLK(clk), 55 | .OUTPUT_ENABLE(1'b0), 56 | .D_OUT_0(tx_lo), 57 | .D_OUT_1(1'b0), 58 | .D_IN_0(), 59 | .D_IN_1() 60 | ); 61 | 62 | endmodule // e1_tx_phy 63 | -------------------------------------------------------------------------------- /rtl/e1_wb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_wb.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 wishbone top-level 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_wb #( 15 | parameter integer N = 1, // Number of units 16 | parameter UNIT_HAS_RX = 1'b1, // 1 bit per unit 17 | parameter UNIT_HAS_TX = 1'b1, // 1 bit per unit 18 | parameter integer LIU = 0, 19 | parameter integer MFW = 7 20 | )( 21 | // IO pads 22 | // Raw PHY 23 | input wire [N-1:0] pad_rx_hi_p, 24 | input wire [N-1:0] pad_rx_hi_n, 25 | input wire [N-1:0] pad_rx_lo_p, 26 | input wire [N-1:0] pad_rx_lo_n, 27 | 28 | output wire [N-1:0] pad_tx_hi, 29 | output wire [N-1:0] pad_tx_lo, 30 | 31 | // LIU 32 | input wire [N-1:0] pad_rx_data, 33 | input wire [N-1:0] pad_rx_clk, 34 | 35 | output wire [N-1:0] pad_tx_data, 36 | output wire [N-1:0] pad_tx_clk, 37 | 38 | // Buffer interface 39 | // E1 RX (write) 40 | output wire [(N*8) -1:0] buf_rx_data, 41 | output wire [(N*5) -1:0] buf_rx_ts, 42 | output wire [(N*4) -1:0] buf_rx_frame, 43 | output wire [(N*MFW)-1:0] buf_rx_mf, 44 | output wire [ N -1:0] buf_rx_we, 45 | input wire [ N -1:0] buf_rx_rdy, 46 | 47 | // E1 TX (read) 48 | input wire [(N*8) -1:0] buf_tx_data, 49 | output wire [(N*5) -1:0] buf_tx_ts, 50 | output wire [(N*4) -1:0] buf_tx_frame, 51 | output wire [(N*MFW)-1:0] buf_tx_mf, 52 | output wire [ N -1:0] buf_tx_re, 53 | input wire [ N -1:0] buf_tx_rdy, 54 | 55 | // Wishbone slave 56 | input wire [ 7:0] wb_addr, 57 | output reg [15:0] wb_rdata, 58 | input wire [15:0] wb_wdata, 59 | input wire wb_we, 60 | input wire wb_cyc, 61 | output reg wb_ack, 62 | 63 | // External strobes 64 | output reg irq, 65 | output wire [4*N-1:0] mon_tick, 66 | 67 | // Common 68 | input wire clk, 69 | input wire rst 70 | ); 71 | 72 | // -------------------------------------------------------------------------- 73 | // Common part 74 | // -------------------------------------------------------------------------- 75 | 76 | localparam integer MB = $clog2(2*N); 77 | 78 | 79 | // Signals 80 | // ------- 81 | 82 | // Bus access 83 | wire bus_clr; 84 | 85 | wire [ 0:0] bus_addr_lsb; 86 | wire [15:0] bus_rdata_rx[0:N-1]; 87 | wire [15:0] bus_rdata_tx[0:N-1]; 88 | reg [15:0] bus_rdata; 89 | wire [15:0] bus_wdata; 90 | wire bus_we; 91 | 92 | // Loopback paths 93 | wire [N-1:0] lb_bit; 94 | wire [N-1:0] lb_valid; 95 | 96 | // IRQs 97 | wire [N-1:0] irq_rx; 98 | wire [N-1:0] irq_tx; 99 | 100 | 101 | // Bus access 102 | // ---------- 103 | 104 | // Ack is always 1 cycle after access 105 | always @(posedge clk) 106 | wb_ack <= wb_cyc & ~wb_ack; 107 | 108 | assign bus_clr = ~wb_cyc | wb_ack; 109 | 110 | // Direct map of some signals to custom local bus 111 | assign bus_addr_lsb = wb_addr[0]; 112 | assign bus_wdata = wb_wdata; 113 | assign bus_we = wb_we; 114 | 115 | // Read MUX 116 | always @(*) 117 | begin : rdata_or 118 | integer j; 119 | bus_rdata = 0; 120 | for (j=0; j 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_wb_rx #( 15 | parameter integer LIU = 0, 16 | parameter integer MFW = 7 17 | )( 18 | // IO pads 19 | // Raw PHY 20 | input wire pad_rx_hi_p, 21 | input wire pad_rx_hi_n, 22 | input wire pad_rx_lo_p, 23 | input wire pad_rx_lo_n, 24 | 25 | // LIU 26 | input wire pad_rx_data, 27 | input wire pad_rx_clk, 28 | 29 | // Buffer interface 30 | output wire [7:0] buf_rx_data, 31 | output wire [4:0] buf_rx_ts, 32 | output wire [3:0] buf_rx_frame, 33 | output wire [MFW-1:0] buf_rx_mf, 34 | output wire buf_rx_we, 35 | input wire buf_rx_rdy, 36 | 37 | // Bus interface 38 | input wire bus_addr_sel, 39 | input wire [0:0] bus_addr_lsb, 40 | input wire [15:0] bus_wdata, 41 | output wire [15:0] bus_rdata, 42 | input wire bus_clr, 43 | input wire bus_we, 44 | 45 | // RX/TX cross status 46 | output reg [1:0] tx_crc_e_auto, 47 | input wire tx_crc_e_ack, 48 | 49 | // External strobes 50 | output wire irq, 51 | output wire [2:0] mon_tick, 52 | 53 | // Loopback path 54 | output wire lb_bit, 55 | output wire lb_valid, 56 | 57 | // Common 58 | input wire clk, 59 | input wire rst 60 | ); 61 | 62 | // Signals 63 | // ------- 64 | 65 | // CSRs and bus access 66 | reg crx_wren; 67 | reg crx_clear; 68 | 69 | wire [15:0] bus_rd_rx_status; 70 | wire [15:0] bus_rd_rx_bdout; 71 | 72 | // FIFOs 73 | // BD RX In 74 | wire [MFW-1:0] bri_di; 75 | wire [MFW-1:0] bri_do; 76 | reg bri_wren; 77 | wire bri_rden; 78 | wire bri_full; 79 | wire bri_empty; 80 | 81 | // BD RX Out 82 | wire [MFW+1:0] bro_di; 83 | wire [MFW+1:0] bro_do; 84 | wire bro_wren; 85 | reg bro_rden; 86 | wire bro_full; 87 | wire bro_empty; 88 | 89 | // Control 90 | reg rx_rst; 91 | reg rx_enabled; 92 | reg [1:0] rx_mode; 93 | wire rx_aligned; 94 | reg rx_overflow; 95 | 96 | // BD interface 97 | wire [MFW-1:0] bdrx_mf; 98 | wire [1:0] bdrx_crc_e; 99 | wire bdrx_valid; 100 | wire bdrx_done; 101 | wire bdrx_miss; 102 | 103 | 104 | // CSRs & FIFO bus access 105 | // ---------------------- 106 | 107 | // Control WrEn 108 | always @(posedge clk) 109 | if (bus_clr | ~bus_we) begin 110 | crx_wren <= 1'b0; 111 | crx_clear <= 1'b0; 112 | end else begin 113 | crx_wren <= bus_addr_sel & (bus_addr_lsb == 1'b0); 114 | crx_clear <= bus_addr_sel & (bus_addr_lsb == 1'b0) & bus_wdata[12]; 115 | end 116 | 117 | // Control regs 118 | always @(posedge clk or posedge rst) 119 | if (rst) begin 120 | rx_mode <= 2'b00; 121 | rx_enabled <= 1'b0; 122 | end else if (crx_wren) begin 123 | rx_mode <= bus_wdata[2:1]; 124 | rx_enabled <= bus_wdata[0]; 125 | end 126 | 127 | // Status data 128 | assign bus_rd_rx_status = { 129 | 3'b000, 130 | rx_overflow, 131 | bro_full, 132 | bro_empty, 133 | bri_full, 134 | bri_empty, 135 | 6'b000000, 136 | rx_aligned, 137 | rx_enabled 138 | }; 139 | 140 | // BD FIFO WrEn / RdEn 141 | // (note we must mask on full/empty here to be consistent with what we 142 | // return in the data !) 143 | always @(posedge clk) 144 | if (bus_clr) begin 145 | bri_wren <= 1'b0; 146 | bro_rden <= 1'b0; 147 | end else begin 148 | bri_wren <= bus_we & ~bri_full & bus_addr_sel & (bus_addr_lsb == 1'b1); 149 | bro_rden <= ~bus_we & ~bro_empty & bus_addr_sel & (bus_addr_lsb == 1'b1); 150 | end 151 | 152 | // BD FIFO Data 153 | assign bri_di = bus_wdata[MFW-1:0]; 154 | 155 | assign bus_rd_rx_bdout = { ~bro_empty, bro_do[MFW+1:MFW], {(13-MFW){1'b0}}, bro_do[MFW-1:0] }; 156 | 157 | // Read Mux 158 | assign bus_rdata = bus_addr_sel ? ( bus_addr_lsb[0] ? bus_rd_rx_bdout : bus_rd_rx_status ) : 16'h0000; 159 | // { 16{bus_addr_sel} } & (bus_addr_lsb[0] ? bus_rd_rx_bdout : bus_rd_rx_status); 160 | 161 | 162 | // BD fifos 163 | // -------- 164 | 165 | // BD RX In 166 | fifo_sync_shift #( 167 | .DEPTH(4), 168 | .WIDTH(MFW) 169 | ) bd_rx_in_I ( 170 | .wr_data(bri_di), 171 | .wr_ena(bri_wren), 172 | .wr_full(bri_full), 173 | .rd_data(bri_do), 174 | .rd_ena(bri_rden), 175 | .rd_empty(bri_empty), 176 | .clk(clk), 177 | .rst(rst) 178 | ); 179 | 180 | // BD RX Out 181 | fifo_sync_shift #( 182 | .DEPTH(4), 183 | .WIDTH(MFW+2) 184 | ) bd_rx_out_I ( 185 | .wr_data(bro_di), 186 | .wr_ena(bro_wren), 187 | .wr_full(bro_full), 188 | .rd_data(bro_do), 189 | .rd_ena(bro_rden), 190 | .rd_empty(bro_empty), 191 | .clk(clk), 192 | .rst(rst) 193 | ); 194 | 195 | 196 | // RX submodule 197 | // ------------ 198 | 199 | // RX core 200 | e1_rx #( 201 | .LIU(LIU), 202 | .MFW(MFW) 203 | ) rx_I ( 204 | .pad_rx_hi_p(pad_rx_hi_p), 205 | .pad_rx_hi_n(pad_rx_hi_n), 206 | .pad_rx_lo_p(pad_rx_lo_p), 207 | .pad_rx_lo_n(pad_rx_lo_n), 208 | .pad_rx_data(pad_rx_data), 209 | .pad_rx_clk(pad_rx_clk), 210 | .buf_data(buf_rx_data), 211 | .buf_ts(buf_rx_ts), 212 | .buf_frame(buf_rx_frame), 213 | .buf_mf(buf_rx_mf), 214 | .buf_we(buf_rx_we), 215 | .buf_rdy(buf_rx_rdy), 216 | .bd_mf(bdrx_mf), 217 | .bd_crc_e(bdrx_crc_e), 218 | .bd_valid(bdrx_valid), 219 | .bd_done(bdrx_done), 220 | .bd_miss(bdrx_miss), 221 | .lb_bit(lb_bit), 222 | .lb_valid(lb_valid), 223 | .ctrl_mode_mf(rx_mode[0]), 224 | .status_aligned(rx_aligned), 225 | .mon_tick(mon_tick), 226 | .clk(clk), 227 | .rst(rx_rst) 228 | ); 229 | 230 | // BD FIFO interface 231 | assign bdrx_mf = bri_do; 232 | assign bdrx_valid = ~bri_empty; 233 | 234 | assign bri_rden = bdrx_done; 235 | 236 | assign bro_di = { bdrx_crc_e, bdrx_mf }; 237 | assign bro_wren = ~bro_full & bdrx_done; 238 | 239 | // Control logic 240 | // Local reset 241 | always @(posedge clk or posedge rst) 242 | if (rst) 243 | rx_rst <= 1'b1; 244 | else 245 | rx_rst <= ~rx_enabled; 246 | 247 | // Overflow 248 | always @(posedge clk or posedge rst) 249 | if (rst) 250 | rx_overflow <= 1'b0; 251 | else 252 | rx_overflow <= (rx_overflow & ~crx_clear) | bdrx_miss; 253 | 254 | // Generate auto E bits for TX side 255 | always @(posedge clk) 256 | tx_crc_e_auto <= (tx_crc_e_ack ? {2{rx_aligned}} : tx_crc_e_auto) & (bdrx_done ? bdrx_crc_e : 2'b11); 257 | 258 | 259 | // External strobes 260 | // ---------------- 261 | 262 | assign irq = ~bro_empty | rx_overflow; 263 | 264 | endmodule // e1_wb_rx 265 | -------------------------------------------------------------------------------- /rtl/e1_wb_tx.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_wb_tx.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * E1 wishbone TX submodule - NOT MEANT TO BE USED INDEPENDENTLY 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module e1_wb_tx #( 15 | parameter integer LIU = 0, 16 | parameter integer MFW = 7 17 | )( 18 | // IO pads 19 | // Raw PHY 20 | output wire pad_tx_hi, 21 | output wire pad_tx_lo, 22 | 23 | // LIU 24 | output wire pad_tx_data, 25 | output wire pad_tx_clk, 26 | 27 | // Buffer interface 28 | input wire [7:0] buf_tx_data, 29 | output wire [4:0] buf_tx_ts, 30 | output wire [3:0] buf_tx_frame, 31 | output wire [MFW-1:0] buf_tx_mf, 32 | output wire buf_tx_re, 33 | input wire buf_tx_rdy, 34 | 35 | // Bus interface 36 | input wire bus_addr_sel, 37 | input wire [0:0] bus_addr_lsb, 38 | input wire [15:0] bus_wdata, 39 | output wire [15:0] bus_rdata, 40 | input wire bus_clr, 41 | input wire bus_we, 42 | 43 | // RX/TX cross status 44 | input wire [1:0] tx_crc_e_auto, 45 | output wire tx_crc_e_ack, 46 | 47 | // External strobes 48 | output wire irq, 49 | output wire mon_tick, 50 | 51 | // Loopback path 52 | input wire [1:0] lb_bit, 53 | input wire [1:0] lb_valid, 54 | 55 | // Common 56 | input wire clk, 57 | input wire rst 58 | ); 59 | 60 | // Signals 61 | // ------- 62 | 63 | // CSRs and bus access 64 | reg ctx_wren; 65 | reg ctx_clear; 66 | 67 | wire [15:0] bus_rd_tx_status; 68 | wire [15:0] bus_rd_tx_bdout; 69 | 70 | // FIFOs 71 | // BD TX In 72 | wire [MFW+1:0] bti_di; 73 | wire [MFW+1:0] bti_do; 74 | reg bti_wren; 75 | wire bti_rden; 76 | wire bti_full; 77 | wire bti_empty; 78 | 79 | // BD TX Out 80 | wire [MFW-1:0] bto_di; 81 | wire [MFW-1:0] bto_do; 82 | wire bto_wren; 83 | reg bto_rden; 84 | wire bto_full; 85 | wire bto_empty; 86 | 87 | // Control 88 | reg tx_rst; 89 | reg tx_enabled; 90 | reg [1:0] tx_mode; 91 | reg tx_time_src; 92 | reg tx_alarm; 93 | reg [1:0] tx_loopback; 94 | reg tx_underflow; 95 | 96 | // BD interface 97 | wire [MFW-1:0] bdtx_mf; 98 | wire [1:0] bdtx_crc_e; 99 | wire bdtx_valid; 100 | wire bdtx_done; 101 | wire bdtx_miss; 102 | 103 | // Timing 104 | wire ext_tick; 105 | wire int_tick; 106 | 107 | 108 | // CSRs & FIFO bus access 109 | // ---------------------- 110 | 111 | // Control WrEn 112 | always @(posedge clk) 113 | if (bus_clr | ~bus_we) begin 114 | ctx_wren <= 1'b0; 115 | ctx_clear <= 1'b0; 116 | end else begin 117 | ctx_wren <= bus_addr_sel & (bus_addr_lsb == 1'b0); 118 | ctx_clear <= bus_addr_sel & (bus_addr_lsb == 1'b0) & bus_wdata[12]; 119 | end 120 | 121 | // Control regs 122 | always @(posedge clk or posedge rst) 123 | if (rst) begin 124 | tx_loopback <= 2'b00; 125 | tx_alarm <= 1'b0; 126 | tx_time_src <= 1'b0; 127 | tx_mode <= 2'b00; 128 | tx_enabled <= 1'b0; 129 | end else if (ctx_wren) begin 130 | tx_loopback <= bus_wdata[6:5]; 131 | tx_alarm <= bus_wdata[4]; 132 | tx_time_src <= bus_wdata[3]; 133 | tx_mode <= bus_wdata[2:1]; 134 | tx_enabled <= bus_wdata[0]; 135 | end 136 | 137 | // Status data 138 | assign bus_rd_tx_status = { 139 | 3'b000, 140 | tx_underflow, 141 | bto_full, 142 | bto_empty, 143 | bti_full, 144 | bti_empty, 145 | 7'b0000000, 146 | tx_enabled 147 | }; 148 | 149 | // BD FIFO WrEn / RdEn 150 | // (note we must mask on full/empty here to be consistent with what we 151 | // return in the data !) 152 | always @(posedge clk) 153 | if (bus_clr) begin 154 | bti_wren <= 1'b0; 155 | bto_rden <= 1'b0; 156 | end else begin 157 | bti_wren <= bus_we & ~bti_full & bus_addr_sel & (bus_addr_lsb == 1'b1); 158 | bto_rden <= ~bus_we & ~bto_empty & bus_addr_sel & (bus_addr_lsb == 1'b1); 159 | end 160 | 161 | // BD FIFO Data 162 | assign bti_di = { bus_wdata[14:13], bus_wdata[MFW-1:0] }; 163 | 164 | assign bus_rd_tx_bdout = { ~bto_empty, {(15-MFW){1'b0}}, bto_do[MFW-1:0] }; 165 | 166 | // Read Mux 167 | assign bus_rdata = bus_addr_sel ? ( bus_addr_lsb[0] ? bus_rd_tx_bdout : bus_rd_tx_status ) : 16'h0000; 168 | //assign bus_rdata = { 16{bus_addr_sel} } & (bus_addr_lsb[0] ? bus_rd_tx_bdout : bus_rd_tx_status); 169 | 170 | 171 | // BD fifos 172 | // -------- 173 | 174 | // BD TX In 175 | fifo_sync_shift #( 176 | .DEPTH(4), 177 | .WIDTH(MFW+2) 178 | ) bd_tx_in_I ( 179 | .wr_data(bti_di), 180 | .wr_ena(bti_wren), 181 | .wr_full(bti_full), 182 | .rd_data(bti_do), 183 | .rd_ena(bti_rden), 184 | .rd_empty(bti_empty), 185 | .clk(clk), 186 | .rst(rst) 187 | ); 188 | 189 | // BD TX Out 190 | fifo_sync_shift #( 191 | .DEPTH(4), 192 | .WIDTH(MFW) 193 | ) bd_tx_out_I ( 194 | .wr_data(bto_di), 195 | .wr_ena(bto_wren), 196 | .wr_full(bto_full), 197 | .rd_data(bto_do), 198 | .rd_ena(bto_rden), 199 | .rd_empty(bto_empty), 200 | .clk(clk), 201 | .rst(rst) 202 | ); 203 | 204 | 205 | // TX submodule 206 | // ------------ 207 | 208 | // TX core 209 | e1_tx #( 210 | .LIU(LIU), 211 | .MFW(MFW) 212 | ) tx_I ( 213 | .pad_tx_hi(pad_tx_hi), 214 | .pad_tx_lo(pad_tx_lo), 215 | .pad_tx_data(pad_tx_data), 216 | .pad_tx_clk(pad_tx_clk), 217 | .buf_data(buf_tx_data), 218 | .buf_ts(buf_tx_ts), 219 | .buf_frame(buf_tx_frame), 220 | .buf_mf(buf_tx_mf), 221 | .buf_re(buf_tx_re), 222 | .buf_rdy(buf_tx_rdy), 223 | .bd_mf(bdtx_mf), 224 | .bd_crc_e(bdtx_crc_e), 225 | .bd_valid(bdtx_valid), 226 | .bd_done(bdtx_done), 227 | .bd_miss(bdtx_miss), 228 | .lb_bit(lb_bit[tx_loopback[1]]), 229 | .lb_valid(lb_valid[tx_loopback[1]]), 230 | .ext_tick(ext_tick), 231 | .int_tick(int_tick), 232 | .ctrl_time_src(tx_time_src), 233 | .ctrl_do_framing(tx_mode != 2'b00), 234 | .ctrl_do_crc4(tx_mode[1]), 235 | .ctrl_loopback(tx_loopback[0]), 236 | .alarm(tx_alarm), 237 | .clk(clk), 238 | .rst(tx_rst) 239 | ); 240 | 241 | assign ext_tick = lb_valid[0]; 242 | 243 | // Auto E-bit tracking 244 | assign tx_crc_e_ack = bdtx_done; 245 | 246 | // BD FIFO interface 247 | assign bdtx_mf = bti_do[MFW-1:0]; 248 | assign bdtx_crc_e = (tx_mode == 2'b11) ? tx_crc_e_auto : bti_do[MFW+1:MFW]; 249 | assign bdtx_valid = ~bti_empty; 250 | 251 | assign bti_rden = bdtx_done; 252 | 253 | assign bto_di = bdtx_mf; 254 | assign bto_wren = ~bto_full & bdtx_done; 255 | 256 | // Control logic 257 | // Local reset 258 | always @(posedge clk or posedge rst) 259 | if (rst) 260 | tx_rst <= 1'b1; 261 | else 262 | tx_rst <= ~tx_enabled; 263 | 264 | // Underflow 265 | always @(posedge clk or posedge rst) 266 | if (rst) 267 | tx_underflow <= 1'b0; 268 | else 269 | tx_underflow <= (tx_underflow & ~ctx_clear) | bdtx_miss; 270 | 271 | 272 | // External strobes 273 | // ---------------- 274 | 275 | assign irq = ~bto_empty | tx_underflow; 276 | assign mon_tick = int_tick; /* tick used for TX */ 277 | 278 | endmodule // e1_wb_tx 279 | -------------------------------------------------------------------------------- /rtl/hdb3_dec.v: -------------------------------------------------------------------------------- 1 | /* 2 | * hdb3_dec.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * HDB3 symbols -> bit decoding as described in G.703 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module hdb3_dec ( 15 | // Input 16 | input wire in_pos, 17 | input wire in_neg, 18 | input wire in_valid, 19 | 20 | // Output 21 | output wire out_data, 22 | output reg out_valid, 23 | 24 | // Common 25 | input wire clk, 26 | input wire rst 27 | ); 28 | 29 | // Signals 30 | wire violation; 31 | reg [3:0] data; 32 | reg pstate; // Pulse state 33 | 34 | // Output 35 | assign out_data = data[3]; 36 | 37 | always @(posedge clk) 38 | out_valid <= in_valid; 39 | 40 | // Main logic 41 | assign violation = (in_pos & pstate) | (in_neg & ~pstate); 42 | 43 | always @(posedge clk) 44 | begin 45 | if (rst) begin 46 | // Reset state 47 | data <= 4'h0; 48 | pstate <= 1'b0; 49 | 50 | end else if (in_valid) begin 51 | if (in_pos ^ in_neg) begin 52 | // Is it a violation ? 53 | if (violation) begin 54 | // Violation 55 | data <= 4'h0; 56 | pstate <= pstate; 57 | 58 | end else begin 59 | // Normal data (or possibly balancing pulse that will be 60 | // post-corrected) 61 | data <= { data[2:0], 1'b1 }; 62 | pstate <= pstate ^ 1; 63 | end 64 | end else begin 65 | // Zero (or error, we map to 0) 66 | data <= { data[2:0], 1'b0 }; 67 | pstate <= pstate; 68 | end 69 | end 70 | end 71 | 72 | endmodule // hdb3_dec 73 | -------------------------------------------------------------------------------- /rtl/hdb3_enc.v: -------------------------------------------------------------------------------- 1 | /* 2 | * hdb3_enc.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * HDB3 bit ->symbols encoding as described in G.703 7 | * 8 | * Copyright (C) 2019-2020 Sylvain Munaut 9 | * SPDX-License-Identifier: CERN-OHL-W-2.0 10 | */ 11 | 12 | `default_nettype none 13 | 14 | module hdb3_enc ( 15 | // Input 16 | input wire in_data, 17 | input wire in_valid, 18 | 19 | // Output 20 | output wire out_pos, 21 | output wire out_neg, 22 | output reg out_valid, 23 | 24 | // Common 25 | input wire clk, 26 | input wire rst 27 | ); 28 | 29 | // Signals 30 | reg [3:0] d_pos; 31 | reg [3:0] d_neg; 32 | 33 | reg [1:0] zcnt; // Zero-Count 34 | reg pstate; // Pulse state 35 | reg vstate; // Violation state 36 | 37 | // Output 38 | assign out_pos = d_pos[3]; 39 | assign out_neg = d_neg[3]; 40 | 41 | always @(posedge clk) 42 | out_valid <= in_valid; 43 | 44 | // Main logic 45 | always @(posedge clk) 46 | begin 47 | if (rst) begin 48 | // Reset state 49 | d_pos <= 4'h0; 50 | d_neg <= 4'h0; 51 | zcnt <= 2'b00; 52 | pstate <= 1'b0; 53 | vstate <= 1'b0; 54 | 55 | end else if (in_valid) begin 56 | // Check for 4 zeros 57 | if ((zcnt == 2'b11) && (in_data == 1'b0)) begin 58 | // This is a run, handle special case 59 | // But need to check if it's 000V or B00V 60 | if (pstate == vstate) begin 61 | // Pulse State is the same state as the last violation 62 | // state. So this next violation state is going to be 63 | // opposite polarity, so no DC to compensate -> 000V 64 | 65 | // New data: Violation bit 66 | d_pos[0] <= pstate; 67 | d_neg[0] <= ~pstate; 68 | 69 | // Shift reg 70 | d_pos[3:1] <= d_pos[2:0]; 71 | d_neg[3:1] <= d_neg[2:0]; 72 | 73 | // Zero count: Reset 74 | zcnt <= 2'b00; 75 | 76 | // Pulse state tracking 77 | pstate <= pstate; 78 | 79 | // Violation state tracking 80 | vstate <= vstate ^ 1; 81 | 82 | end else begin 83 | // Pulse State is the opposite state as the last violation 84 | // state. So this next violation would be the same 85 | // polarity ... need to use B00V to avoid DC 86 | 87 | // New data: Violation bit 88 | d_pos[0] <= ~pstate; 89 | d_neg[0] <= pstate; 90 | 91 | // Shift reg 92 | d_pos[2:1] <= d_pos[1:0]; 93 | d_neg[2:1] <= d_neg[1:0]; 94 | 95 | // Balancing bit 96 | d_pos[3] <= ~pstate; 97 | d_neg[3] <= pstate; 98 | 99 | // Zero count: Reset 100 | zcnt <= 2'b00; 101 | 102 | // Pulse state tracking 103 | pstate <= pstate ^ 1; 104 | 105 | // Violation state tracking 106 | vstate <= vstate ^ 1; 107 | end 108 | end else begin 109 | // Normal case 110 | // New data 111 | d_pos[0] <= in_data & ~pstate; 112 | d_neg[0] <= in_data & pstate; 113 | 114 | // Shift reg 115 | d_pos[3:1] <= d_pos[2:0]; 116 | d_neg[3:1] <= d_neg[2:0]; 117 | 118 | // Zero count 119 | if (in_data == 1'b0) 120 | zcnt <= zcnt + 1; 121 | else 122 | zcnt <= 2'b00; 123 | 124 | // Pulse state tracking 125 | pstate <= pstate ^ in_data; 126 | 127 | // Violation state tracking 128 | vstate <= vstate; 129 | end 130 | end 131 | end 132 | 133 | endmodule // hdb3_enc 134 | -------------------------------------------------------------------------------- /sim/e1_buf_if_wb_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_buf_if_wb_tb.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * Copyright (C) 2019-2020 Sylvain Munaut 7 | * SPDX-License-Identifier: CERN-OHL-W-2.0 8 | */ 9 | 10 | `default_nettype none 11 | `timescale 1ns / 100ps 12 | 13 | module e1_buf_if_wb_tb; 14 | 15 | // Signals 16 | reg rst = 1; 17 | reg clk = 1; 18 | 19 | // Wishbone master 20 | wire [13:0] wb_addr; 21 | wire [31:0] wb_rdata; 22 | wire [31:0] wb_wdata; 23 | wire [ 3:0] wb_wmsk; 24 | wire wb_cyc; 25 | wire wb_we; 26 | reg wb_ack; 27 | 28 | // Buffer interface 29 | wire [15:0] buf_rx_data; 30 | wire [ 9:0] buf_rx_ts; 31 | wire [ 7:0] buf_rx_frame; 32 | wire [13:0] buf_rx_mf; 33 | wire [ 1:0] buf_rx_we; 34 | wire [ 1:0] buf_rx_rdy; 35 | 36 | wire [15:0] buf_tx_data; 37 | wire [ 9:0] buf_tx_ts; 38 | wire [ 7:0] buf_tx_frame; 39 | wire [13:0] buf_tx_mf; 40 | wire [ 1:0] buf_tx_re; 41 | wire [ 1:0] buf_tx_rdy; 42 | 43 | // Setup recording 44 | initial begin 45 | $dumpfile("e1_buf_if_wb_tb.vcd"); 46 | $dumpvars(0,e1_buf_if_wb_tb); 47 | end 48 | 49 | // Reset pulse 50 | initial begin 51 | # 31 rst = 0; 52 | # 20000 $finish; 53 | end 54 | 55 | // Clocks 56 | always #5 clk = !clk; 57 | 58 | // DUT 59 | e1_buf_if_wb #( 60 | .N(2), 61 | .UNIT_HAS_RX(2'b01), 62 | .UNIT_HAS_TX(2'b11), 63 | .MFW(7), 64 | .DW(32) 65 | ) e1_I ( 66 | .wb_addr (wb_addr), 67 | .wb_rdata (wb_rdata), 68 | .wb_wdata (wb_wdata), 69 | .wb_wmsk (wb_wmsk), 70 | .wb_cyc (wb_cyc), 71 | .wb_we (wb_we), 72 | .wb_ack (wb_ack), 73 | .buf_rx_data (buf_rx_data), 74 | .buf_rx_ts (buf_rx_ts), 75 | .buf_rx_frame(buf_rx_frame), 76 | .buf_rx_mf (buf_rx_mf), 77 | .buf_rx_we (buf_rx_we), 78 | .buf_rx_rdy (buf_rx_rdy), 79 | .buf_tx_data (buf_tx_data), 80 | .buf_tx_ts (buf_tx_ts), 81 | .buf_tx_frame(buf_tx_frame), 82 | .buf_tx_mf (buf_tx_mf), 83 | .buf_tx_re (buf_tx_re), 84 | .buf_tx_rdy (buf_tx_rdy), 85 | .clk (clk), 86 | .rst (rst) 87 | ); 88 | 89 | // Dummy wishbone responder 90 | always @(posedge clk) 91 | wb_ack <= wb_cyc & ~wb_ack; 92 | 93 | assign wb_rdata = 32'h01234567; 94 | 95 | // 96 | reg rdy; 97 | reg [31:0] cnt; 98 | reg [ 3:0] sub; 99 | 100 | always @(posedge clk) 101 | if (rst) 102 | rdy <= 1'b0; 103 | else 104 | rdy <= 1'b1; 105 | 106 | always @(posedge clk) 107 | if (rst) 108 | cnt <= 0; 109 | else if (wb_ack) 110 | cnt <= cnt + 1; 111 | 112 | always @(posedge clk) 113 | if (rst) 114 | sub <= 0; 115 | else 116 | sub <= { 117 | ($random & 7) == 0, 118 | ($random & 7) == 0, 119 | ($random & 7) == 0, 120 | ($random & 7) == 0 121 | }; 122 | 123 | // RX submit 124 | assign buf_rx_data = { 8'hxx, 8'h34 }; 125 | assign buf_rx_ts = { 5'dx, cnt[4:0] }; 126 | assign buf_rx_frame = { 4'dx, cnt[8:5] }; 127 | assign buf_rx_mf = { 7'dx, cnt[15:9] }; 128 | assign buf_rx_we = buf_rx_rdy & { 1'b0, sub[0] }; 129 | 130 | // TX submit 131 | assign buf_tx_ts = { 5'd7, cnt[4:0] }; 132 | assign buf_tx_frame = { 4'd2, cnt[8:5] }; 133 | assign buf_tx_mf = { 7'd100, cnt[15:9] }; 134 | assign buf_tx_re = buf_tx_rdy & sub[3:2]; 135 | 136 | endmodule // e1_buf_if_wb_tb 137 | -------------------------------------------------------------------------------- /sim/e1_crc4_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_crc4_tb.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * Copyright (C) 2019-2020 Sylvain Munaut 7 | * SPDX-License-Identifier: CERN-OHL-W-2.0 8 | */ 9 | 10 | `default_nettype none 11 | `timescale 1ns / 100ps 12 | 13 | module e1_crc4_tb; 14 | 15 | // Signals 16 | reg rst = 1; 17 | reg clk = 1; 18 | 19 | reg [31:0] data; 20 | 21 | wire in_bit; 22 | reg in_valid; 23 | reg in_first; 24 | 25 | wire [3:0] crc; 26 | 27 | // Setup recording 28 | initial begin 29 | $dumpfile("e1_crc4_tb.vcd"); 30 | $dumpvars(0,e1_crc4_tb); 31 | end 32 | 33 | // Reset pulse 34 | initial begin 35 | # 31 rst = 0; 36 | # 20000 $finish; 37 | end 38 | 39 | // Clocks 40 | always #5 clk = !clk; 41 | 42 | // DUT 43 | e1_crc4 dut_I ( 44 | .in_bit(in_bit), 45 | .in_first(in_first), 46 | .in_valid(in_valid), 47 | .out_crc4(crc), 48 | .clk(clk), 49 | .rst(rst) 50 | ); 51 | 52 | // Data feed 53 | always @(posedge clk) 54 | if (rst) 55 | in_valid <= 1'b0; 56 | else 57 | in_valid <= 1'b1; 58 | 59 | always @(posedge clk) 60 | if (rst) 61 | in_first <= 1'b1; 62 | else if (in_valid) 63 | in_first <= 1'b0; 64 | 65 | always @(posedge clk) 66 | if (rst) 67 | //data <= 32'h600dbabe; 68 | data <= 32'h0badbabe; 69 | else if (in_valid) 70 | data <= { data[31:0], 1'b0 }; 71 | 72 | assign in_bit = data[31]; 73 | 74 | endmodule // e1_crc4_tb 75 | -------------------------------------------------------------------------------- /sim/e1_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_tb.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * Copyright (C) 2019-2020 Sylvain Munaut 7 | * SPDX-License-Identifier: CERN-OHL-W-2.0 8 | */ 9 | 10 | `default_nettype none 11 | `timescale 1ns / 100ps 12 | 13 | module e1_tb; 14 | 15 | // Signals 16 | reg rst = 1; 17 | reg clk_16m = 0; 18 | reg clk_30m72 = 0; 19 | 20 | reg [7:0] in_file_data; 21 | reg in_file_valid; 22 | reg in_file_done; 23 | 24 | wire e1_in_tip; 25 | wire e1_in_ring; 26 | 27 | wire e1_bit; 28 | wire e1_valid; 29 | 30 | wire e1_out_tip; 31 | wire e1_out_ring; 32 | 33 | wire df_valid; 34 | wire [7:0] df_data; 35 | wire [4:0] df_ts; 36 | 37 | // Setup recording 38 | initial begin 39 | $dumpfile("e1_tb.vcd"); 40 | $dumpvars(0,e1_tb); 41 | end 42 | 43 | // Reset pulse 44 | initial begin 45 | # 200 rst = 0; 46 | # 10000000 $finish; 47 | end 48 | 49 | // Clocks 50 | always #31.25 clk_16m = !clk_16m; 51 | always #16.276 clk_30m72 = !clk_30m72; 52 | 53 | // DUT 54 | e1_rx rx_I ( 55 | .pad_rx_hi_p( e1_in_ring), 56 | .pad_rx_hi_n(~e1_in_ring), 57 | .pad_rx_lo_p( e1_in_tip), 58 | .pad_rx_lo_n(~e1_in_tip), 59 | .lb_bit(e1_bit), 60 | .lb_valid(e1_valid), 61 | .ctrl_mode_mf(1'b0), 62 | .clk(clk_30m72), 63 | .rst(rst) 64 | ); 65 | 66 | e1_tx tx_I ( 67 | .pad_tx_hi(e1_out_ring), 68 | .pad_tx_lo(e1_out_tip), 69 | .lb_bit(e1_bit), 70 | .lb_valid(e1_valid), 71 | .ctrl_time_src(1'b0), 72 | .ctrl_do_framing(1'b0), 73 | .ctrl_do_crc4(1'b0), 74 | .ctrl_loopback(1'b1), 75 | .alarm(1'b0), 76 | .clk(clk_30m72), 77 | .rst(rst) 78 | ); 79 | 80 | e1_rx_deframer rx_deframer_I ( 81 | .in_bit(e1_bit), 82 | .in_valid(e1_valid), 83 | .out_data(df_data), 84 | .out_valid(df_valid), 85 | .out_ts(df_ts), 86 | .clk(clk_30m72), 87 | .rst(rst) 88 | ); 89 | 90 | // Read file 91 | integer fh_in, rv; 92 | 93 | initial 94 | fh_in = $fopen("../data/capture_e1_raw.bin", "rb"); 95 | 96 | always @(posedge clk_16m) 97 | begin 98 | if (rst) begin 99 | in_file_data <= 8'h00; 100 | in_file_valid <= 1'b0; 101 | in_file_done <= 1'b0; 102 | end else begin 103 | if (!in_file_done) begin 104 | rv = $fread(in_file_data, fh_in); 105 | in_file_valid <= (rv == 1); 106 | in_file_done <= (rv != 1); 107 | end else begin 108 | in_file_data <= 8'h00; 109 | in_file_valid <= 1'b0; 110 | in_file_done <= 1'b1; 111 | end 112 | end 113 | end 114 | 115 | // Write file 116 | integer fh_out; 117 | 118 | initial 119 | fh_out = $fopen("/tmp/e1.txt", "w"); 120 | 121 | always @(posedge clk_30m72) 122 | begin 123 | if (e1_valid) begin 124 | $fwrite(fh_out, "%d", e1_bit); 125 | end 126 | end 127 | 128 | // Input 129 | assign e1_in_tip = in_file_data[0] & in_file_valid; 130 | assign e1_in_ring = in_file_data[1] & in_file_valid; 131 | 132 | endmodule // e1_tb 133 | -------------------------------------------------------------------------------- /sim/e1_tx_framer_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * e1_tx_framer_tb.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * Copyright (C) 2019-2020 Sylvain Munaut 7 | * SPDX-License-Identifier: CERN-OHL-W-2.0 8 | */ 9 | 10 | `default_nettype none 11 | `timescale 1ns / 100ps 12 | 13 | module e1_tx_framer_tb; 14 | 15 | // Signals 16 | reg rst = 1; 17 | reg clk_30m72 = 0; 18 | 19 | wire [7:0] in_data; 20 | wire [1:0] in_crc_e; 21 | wire [3:0] in_frame; 22 | wire [4:0] in_ts; 23 | wire in_mf_first; 24 | wire in_mf_last; 25 | wire in_req; 26 | wire in_rdy; 27 | 28 | wire out_bit; 29 | wire out_valid; 30 | 31 | // Setup recording 32 | initial begin 33 | $dumpfile("e1_tx_framer_tb.vcd"); 34 | $dumpvars(0,e1_tx_framer_tb); 35 | end 36 | 37 | // Reset pulse 38 | initial begin 39 | # 200 rst = 0; 40 | # 10000000 $finish; 41 | end 42 | 43 | // Clocks 44 | always #16.276 clk_30m72 = !clk_30m72; 45 | 46 | // DUT 47 | e1_tx_framer framer_I ( 48 | .in_data(in_data), 49 | .in_crc_e(in_crc_e), 50 | .in_frame(in_frame), 51 | .in_ts(in_ts), 52 | .in_mf_first(in_mf_first), 53 | .in_mf_last(in_mf_last), 54 | .in_req(in_req), 55 | .in_rdy(in_rdy), 56 | .lb_bit(1'b0), 57 | .lb_valid(1'b0), 58 | .out_bit(out_bit), 59 | .out_valid(out_valid), 60 | .ctrl_time_src(1'b0), 61 | .ctrl_do_framing(1'b1), 62 | .ctrl_do_crc4(1'b1), 63 | .ctrl_loopback(1'b0), 64 | .alarm(1'b0), 65 | .ext_tick(1'b0), 66 | .clk(clk_30m72), 67 | .rst(rst) 68 | ); 69 | 70 | reg [7:0] cnt = 8'h00; 71 | 72 | always @(posedge clk_30m72) 73 | if (in_req) 74 | cnt <= cnt + 1; 75 | 76 | assign in_data = in_ts == 5'h10 ? 8'hf9 : cnt; 77 | assign in_crc_e = 2'b11; 78 | assign in_rdy = 1'b1; 79 | 80 | e1_rx_deframer rx_deframer_I ( 81 | .in_bit(out_bit), 82 | .in_valid(out_valid), 83 | .out_data(), 84 | .out_valid(), 85 | .out_ts(), 86 | .clk(clk_30m72), 87 | .rst(rst) 88 | ); 89 | 90 | endmodule // e1_tx_framer_tb 91 | -------------------------------------------------------------------------------- /sim/hdb3_tb.v: -------------------------------------------------------------------------------- 1 | /* 2 | * hdb3_tb.v 3 | * 4 | * vim: ts=4 sw=4 5 | * 6 | * Copyright (C) 2019-2020 Sylvain Munaut 7 | * SPDX-License-Identifier: CERN-OHL-W-2.0 8 | */ 9 | 10 | `default_nettype none 11 | `timescale 1ns / 100ps 12 | 13 | module hdb3_tb; 14 | 15 | // Signals 16 | reg rst = 1; 17 | reg clk = 0; 18 | 19 | reg in_data; 20 | reg in_valid; 21 | wire hdb3_pos; 22 | wire hdb3_neg; 23 | wire hdb3_valid; 24 | wire out_data; 25 | wire out_valid; 26 | 27 | reg [31:0] data; 28 | wire out_data_ref; 29 | wire out_data_err; 30 | 31 | // Setup recording 32 | initial begin 33 | $dumpfile("hdb3_tb.vcd"); 34 | $dumpvars(0,hdb3_tb); 35 | end 36 | 37 | // Reset pulse 38 | initial begin 39 | # 31 rst = 0; 40 | # 10000 $finish; 41 | end 42 | 43 | // Clocks 44 | always #5 clk = !clk; 45 | 46 | // DUT 47 | hdb3_enc dut_enc_I ( 48 | .in_data(in_data), 49 | .in_valid(in_valid), 50 | .out_pos(hdb3_pos), 51 | .out_neg(hdb3_neg), 52 | .out_valid(hdb3_valid), 53 | .clk(clk), 54 | .rst(rst) 55 | ); 56 | 57 | hdb3_dec dut_dec_I ( 58 | .in_pos(hdb3_pos), 59 | .in_neg(hdb3_neg), 60 | .in_valid(hdb3_valid), 61 | .out_data(out_data), 62 | .out_valid(out_valid), 63 | .clk(clk), 64 | .rst(rst) 65 | ); 66 | 67 | // Data feed 68 | always @(posedge clk) 69 | begin 70 | if (rst) begin 71 | in_data <= 1'b0; 72 | in_valid <= 1'b0; 73 | data <= 32'h6ac0c305; 74 | end else begin 75 | in_data <= data[0]; 76 | in_valid <= 1'b1; 77 | data <= { data[0], data[31:1] }; 78 | end 79 | end 80 | 81 | assign out_data_ref = data[23]; 82 | assign out_data_err = out_data_ref != out_data; 83 | 84 | endmodule // hdb3_tb 85 | --------------------------------------------------------------------------------