@@ -123,7 +123,7 @@ def elaborate(self, platform):
123
123
return m
124
124
125
125
126
- class DDRBuffer (io .DDRBuffer ):
126
+ class DDRBufferECP5 (io .DDRBuffer ):
127
127
def elaborate (self , platform ):
128
128
m = Module ()
129
129
@@ -164,9 +164,50 @@ def elaborate(self, platform):
164
164
return m
165
165
166
166
167
- class LatticeECP5Platform (TemplatedPlatform ):
167
+ class DDRBufferMachXO2 (io .DDRBuffer ):
168
+ def elaborate (self , platform ):
169
+ m = Module ()
170
+
171
+ m .submodules .buf = buf = InnerBuffer (self .direction , self .port )
172
+ inv_mask = sum (inv << bit for bit , inv in enumerate (self .port .invert ))
173
+
174
+ if self .direction is not io .Direction .Output :
175
+ i0_inv = Signal (len (self .port ))
176
+ i1_inv = Signal (len (self .port ))
177
+ for bit in range (len (self .port )):
178
+ m .submodules [f"i_ddr{ bit } " ] = Instance ("IDDRXE" ,
179
+ i_SCLK = ClockSignal (self .i_domain ),
180
+ i_RST = Const (0 ),
181
+ i_D = buf .i [bit ],
182
+ o_Q0 = i0_inv [bit ],
183
+ o_Q1 = i1_inv [bit ],
184
+ )
185
+ m .d .comb += self .i [0 ].eq (i0_inv ^ inv_mask )
186
+ m .d .comb += self .i [1 ].eq (i1_inv ^ inv_mask )
187
+
188
+ if self .direction is not io .Direction .Input :
189
+ o0_inv = Signal (len (self .port ))
190
+ o1_inv = Signal (len (self .port ))
191
+ m .d .comb += [
192
+ o0_inv .eq (self .o [0 ] ^ inv_mask ),
193
+ o1_inv .eq (self .o [1 ] ^ inv_mask ),
194
+ ]
195
+ for bit in range (len (self .port )):
196
+ m .submodules [f"o_ddr{ bit } " ] = Instance ("ODDRXE" ,
197
+ i_SCLK = ClockSignal (self .o_domain ),
198
+ i_RST = Const (0 ),
199
+ i_D0 = o0_inv [bit ],
200
+ i_D1 = o1_inv [bit ],
201
+ o_Q = buf .o [bit ],
202
+ )
203
+ _make_oereg (m , self .o_domain , ~ self .oe , buf .t )
204
+
205
+ return m
206
+
207
+
208
+ class LatticePlatform (TemplatedPlatform ):
168
209
"""
169
- .. rubric:: Trellis toolchain
210
+ .. rubric:: Trellis toolchain (ECP5 only)
170
211
171
212
Required tools:
172
213
* ``yosys``
@@ -195,7 +236,7 @@ class LatticeECP5Platform(TemplatedPlatform):
195
236
* ``{{name}}.bit``: binary bitstream.
196
237
* ``{{name}}.svf``: JTAG programming vector.
197
238
198
- .. rubric:: Diamond toolchain
239
+ .. rubric:: Diamond toolchain (ECP5, MachXO2, MachXO3)
199
240
200
241
Required tools:
201
242
* ``pnmainc``
@@ -217,8 +258,11 @@ class LatticeECP5Platform(TemplatedPlatform):
217
258
218
259
Build products:
219
260
* ``{{name}}_impl/{{name}}_impl.htm``: consolidated log.
261
+ * ``{{name}}.jed``: JEDEC fuse file (MachXO2, MachXO3 only).
220
262
* ``{{name}}.bit``: binary bitstream.
221
- * ``{{name}}.svf``: JTAG programming vector.
263
+ * ``{{name}}.svf``: JTAG programming vector (ECP5 only).
264
+ * ``{{name}}_flash.svf``: JTAG programming vector for FLASH programming (MachXO2, MachXO3 only).
265
+ * ``{{name}}_sram.svf``: JTAG programming vector for SRAM programming (MachXO2, MachXO3 only).
222
266
"""
223
267
224
268
toolchain = None # selected when creating platform
@@ -378,6 +422,9 @@ class LatticeECP5Platform(TemplatedPlatform):
378
422
prj_run Map -impl impl
379
423
prj_run PAR -impl impl
380
424
prj_run Export -impl impl -task Bitgen
425
+ {% if family == "machxo2" -%}
426
+ prj_run Export -impl impl -task Jedecgen
427
+ {% endif %}
381
428
{{get_override("script_after_export")|default("# (script_after_export placeholder)")}}
382
429
""" ,
383
430
"{{name}}.lpf" : r"""
@@ -405,7 +452,7 @@ class LatticeECP5Platform(TemplatedPlatform):
405
452
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
406
453
""" ,
407
454
}
408
- _diamond_command_templates = [
455
+ _diamond_command_templates_ecp5 = [
409
456
# These don't have any usable command-line option overrides.
410
457
r"""
411
458
{{invoke_tool("pnmainc")}}
@@ -422,12 +469,54 @@ class LatticeECP5Platform(TemplatedPlatform):
422
469
-if {{name}}_impl/{{name}}_impl.bit -of {{name}}.svf
423
470
""" ,
424
471
]
472
+ _diamond_command_templates_machxo2 = [
473
+ # These don't have any usable command-line option overrides.
474
+ r"""
475
+ {{invoke_tool("pnmainc")}}
476
+ {{name}}.tcl
477
+ """ ,
478
+ r"""
479
+ {{invoke_tool("ddtcmd")}}
480
+ -oft -bit
481
+ -if {{name}}_impl/{{name}}_impl.bit -of {{name}}.bit
482
+ """ ,
483
+ r"""
484
+ {{invoke_tool("ddtcmd")}}
485
+ -oft -jed
486
+ -dev {{platform.device}}-{{platform.speed}}{{platform.package}}{{platform.grade}}
487
+ -if {{name}}_impl/{{name}}_impl.jed -of {{name}}.jed
488
+ """ ,
489
+ r"""
490
+ {{invoke_tool("ddtcmd")}}
491
+ -oft -svfsingle -revd -op "FLASH Erase,Program,Verify"
492
+ -if {{name}}_impl/{{name}}_impl.jed -of {{name}}_flash.svf
493
+ """ ,
494
+ r"""
495
+ {{invoke_tool("ddtcmd")}}
496
+ -oft -svfsingle -revd -op "SRAM Fast Program"
497
+ -if {{name}}_impl/{{name}}_impl.bit -of {{name}}_sram.svf
498
+ """ ,
499
+ ]
425
500
426
501
# Common logic
427
502
428
- def __init__ (self , * , toolchain = "Trellis" ):
503
+ def __init__ (self , * , toolchain = None ):
429
504
super ().__init__ ()
430
505
506
+ device = self .device .lower ()
507
+ if device .startswith (("lfe5" , "lae5" )):
508
+ self .family = "ecp5"
509
+ elif device .startswith (("lcmxo2-" , "lcmxo3l" , "lcmxo3d" , "lamxo2-" , "lamxo3l" , "lamxo3d" , "lfmnx-" )):
510
+ self .family = "machxo2"
511
+ else :
512
+ raise ValueError (f"Device '{ self .device } ' is not recognized" )
513
+
514
+ if toolchain is None :
515
+ if self .family == "ecp5" :
516
+ toolchain = "Trellis"
517
+ else :
518
+ toolchain = "Diamond"
519
+
431
520
assert toolchain in ("Trellis" , "Diamond" )
432
521
self .toolchain = toolchain
433
522
@@ -452,23 +541,46 @@ def command_templates(self):
452
541
if self .toolchain == "Trellis" :
453
542
return self ._trellis_command_templates
454
543
if self .toolchain == "Diamond" :
455
- return self ._diamond_command_templates
544
+ if self .family == "ecp5" :
545
+ return self ._diamond_command_templates_ecp5
546
+ if self .family == "machxo2" :
547
+ return self ._diamond_command_templates_machxo2
456
548
assert False
457
549
550
+ # These numbers were extracted from
551
+ # "MachXO2 sysCLOCK PLL Design and Usage Guide"
552
+ _supported_osch_freqs = [
553
+ 2.08 , 2.15 , 2.22 , 2.29 , 2.38 , 2.46 , 2.56 , 2.66 , 2.77 , 2.89 ,
554
+ 3.02 , 3.17 , 3.33 , 3.50 , 3.69 , 3.91 , 4.16 , 4.29 , 4.43 , 4.59 ,
555
+ 4.75 , 4.93 , 5.12 , 5.32 , 5.54 , 5.78 , 6.05 , 6.33 , 6.65 , 7.00 ,
556
+ 7.39 , 7.82 , 8.31 , 8.58 , 8.87 , 9.17 , 9.50 , 9.85 , 10.23 , 10.64 ,
557
+ 11.08 , 11.57 , 12.09 , 12.67 , 13.30 , 14.00 , 14.78 , 15.65 , 15.65 , 16.63 ,
558
+ 17.73 , 19.00 , 20.46 , 22.17 , 24.18 , 26.60 , 29.56 , 33.25 , 38.00 , 44.33 ,
559
+ 53.20 , 66.50 , 88.67 , 133.00
560
+ ]
561
+
458
562
@property
459
563
def default_clk_constraint (self ):
460
564
if self .default_clk == "OSCG" :
565
+ # Internal high-speed oscillator on ECP5 devices.
461
566
return Clock (310e6 / self .oscg_div )
567
+ if self .default_clk == "OSCH" :
568
+ # Internal high-speed oscillator on MachXO2/MachXO3L devices.
569
+ # It can have a range of frequencies.
570
+ assert self .osch_frequency in self ._supported_osch_freqs
571
+ return Clock (int (self .osch_frequency * 1e6 ))
572
+ # Otherwise, use the defined Clock resource.
462
573
return super ().default_clk_constraint
463
574
464
575
def create_missing_domain (self , name ):
465
- # Lattice ECP5 devices have two global set/reset signals: PUR, which is driven at startup
576
+ # Lattice devices have two global set/reset signals: PUR, which is driven at startup
466
577
# by the configuration logic and unconditionally resets every storage element, and GSR,
467
578
# which is driven by user logic and each storage element may be configured as affected or
468
579
# unaffected by GSR. PUR is purely asynchronous, so even though it is a low-skew global
469
580
# network, its deassertion may violate a setup/hold constraint with relation to a user
470
581
# clock. To avoid this, a GSR/SGSR instance should be driven synchronized to user clock.
471
582
if name == "sync" and self .default_clk is not None :
583
+ using_osch = False
472
584
m = Module ()
473
585
if self .default_clk == "OSCG" :
474
586
if not hasattr (self , "oscg_div" ):
@@ -480,6 +592,14 @@ def create_missing_domain(self, name):
480
592
.format (self .oscg_div ))
481
593
clk_i = Signal ()
482
594
m .submodules += Instance ("OSCG" , p_DIV = self .oscg_div , o_OSC = clk_i )
595
+ elif self .default_clk == "OSCH" :
596
+ osch_freq = self .osch_frequency
597
+ if osch_freq not in self ._supported_osch_freqs :
598
+ raise ValueError ("Frequency {!r} is not valid for OSCH clock. Valid frequencies are {!r}"
599
+ .format (osch_freq , self ._supported_osch_freqs ))
600
+ osch_freq_param = f"{ float (osch_freq ):.2f} "
601
+ clk_i = Signal ()
602
+ m .submodules += [ Instance ("OSCH" , p_NOM_FREQ = osch_freq_param , i_STDBY = Const (0 ), o_OSC = clk_i , o_SEDSTDBY = Signal ()) ]
483
603
else :
484
604
clk_i = self .request (self .default_clk ).i
485
605
if self .default_rst is not None :
@@ -489,7 +609,7 @@ def create_missing_domain(self, name):
489
609
490
610
gsr0 = Signal ()
491
611
gsr1 = Signal ()
492
- # There is no end-of-startup signal on ECP5 , but PUR is released after IOB enable, so
612
+ # There is no end-of-startup signal on Lattice , but PUR is released after IOB enable, so
493
613
# a simple reset synchronizer (with PUR as the asynchronous reset) does the job.
494
614
m .submodules += [
495
615
Instance ("FD1S3AX" , p_GSR = "DISABLED" , i_CK = clk_i , i_D = ~ rst_i , o_Q = gsr0 ),
@@ -512,7 +632,12 @@ def get_io_buffer(self, buffer):
512
632
elif isinstance (buffer , io .FFBuffer ):
513
633
result = FFBuffer (buffer .direction , buffer .port )
514
634
elif isinstance (buffer , io .DDRBuffer ):
515
- result = DDRBuffer (buffer .direction , buffer .port )
635
+ if self .family == "ecp5" :
636
+ result = DDRBufferECP5 (buffer .direction , buffer .port )
637
+ elif self .family == "machxo2" :
638
+ result = DDRBufferMachXO2 (buffer .direction , buffer .port )
639
+ else :
640
+ raise NotImplementedError # :nocov:
516
641
else :
517
642
raise TypeError (f"Unsupported buffer type { buffer !r} " ) # :nocov:
518
643
if buffer .direction is not io .Direction .Output :
@@ -522,5 +647,5 @@ def get_io_buffer(self, buffer):
522
647
result .oe = buffer .oe
523
648
return result
524
649
525
- # CDC primitives are not currently specialized for ECP5 .
650
+ # CDC primitives are not currently specialized for Lattice .
526
651
# While Diamond supports false path constraints; nextpnr-ecp5 does not.
0 commit comments