@@ -123,7 +123,7 @@ def elaborate(self, platform):
123123 return m
124124
125125
126- class DDRBuffer (io .DDRBuffer ):
126+ class DDRBufferECP5 (io .DDRBuffer ):
127127 def elaborate (self , platform ):
128128 m = Module ()
129129
@@ -164,9 +164,50 @@ def elaborate(self, platform):
164164 return m
165165
166166
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 ):
168209 """
169- .. rubric:: Trellis toolchain
210+ .. rubric:: Trellis toolchain (ECP5 only)
170211
171212 Required tools:
172213 * ``yosys``
@@ -195,7 +236,7 @@ class LatticeECP5Platform(TemplatedPlatform):
195236 * ``{{name}}.bit``: binary bitstream.
196237 * ``{{name}}.svf``: JTAG programming vector.
197238
198- .. rubric:: Diamond toolchain
239+ .. rubric:: Diamond toolchain (ECP5, MachXO2, MachXO3)
199240
200241 Required tools:
201242 * ``pnmainc``
@@ -217,8 +258,11 @@ class LatticeECP5Platform(TemplatedPlatform):
217258
218259 Build products:
219260 * ``{{name}}_impl/{{name}}_impl.htm``: consolidated log.
261+ * ``{{name}}.jed``: JEDEC fuse file (MachXO2, MachXO3 only).
220262 * ``{{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).
222266 """
223267
224268 toolchain = None # selected when creating platform
@@ -378,6 +422,9 @@ class LatticeECP5Platform(TemplatedPlatform):
378422 prj_run Map -impl impl
379423 prj_run PAR -impl impl
380424 prj_run Export -impl impl -task Bitgen
425+ {% if family == "machxo2" -%}
426+ prj_run Export -impl impl -task Jedecgen
427+ {% endif %}
381428 {{get_override("script_after_export")|default("# (script_after_export placeholder)")}}
382429 """ ,
383430 "{{name}}.lpf" : r"""
@@ -405,7 +452,7 @@ class LatticeECP5Platform(TemplatedPlatform):
405452 {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
406453 """ ,
407454 }
408- _diamond_command_templates = [
455+ _diamond_command_templates_ecp5 = [
409456 # These don't have any usable command-line option overrides.
410457 r"""
411458 {{invoke_tool("pnmainc")}}
@@ -422,12 +469,54 @@ class LatticeECP5Platform(TemplatedPlatform):
422469 -if {{name}}_impl/{{name}}_impl.bit -of {{name}}.svf
423470 """ ,
424471 ]
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+ ]
425500
426501 # Common logic
427502
428- def __init__ (self , * , toolchain = "Trellis" ):
503+ def __init__ (self , * , toolchain = None ):
429504 super ().__init__ ()
430505
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+
431520 assert toolchain in ("Trellis" , "Diamond" )
432521 self .toolchain = toolchain
433522
@@ -452,23 +541,46 @@ def command_templates(self):
452541 if self .toolchain == "Trellis" :
453542 return self ._trellis_command_templates
454543 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
456548 assert False
457549
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+
458562 @property
459563 def default_clk_constraint (self ):
460564 if self .default_clk == "OSCG" :
565+ # Internal high-speed oscillator on ECP5 devices.
461566 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.
462573 return super ().default_clk_constraint
463574
464575 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
466577 # by the configuration logic and unconditionally resets every storage element, and GSR,
467578 # which is driven by user logic and each storage element may be configured as affected or
468579 # unaffected by GSR. PUR is purely asynchronous, so even though it is a low-skew global
469580 # network, its deassertion may violate a setup/hold constraint with relation to a user
470581 # clock. To avoid this, a GSR/SGSR instance should be driven synchronized to user clock.
471582 if name == "sync" and self .default_clk is not None :
583+ using_osch = False
472584 m = Module ()
473585 if self .default_clk == "OSCG" :
474586 if not hasattr (self , "oscg_div" ):
@@ -480,6 +592,14 @@ def create_missing_domain(self, name):
480592 .format (self .oscg_div ))
481593 clk_i = Signal ()
482594 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 ()) ]
483603 else :
484604 clk_i = self .request (self .default_clk ).i
485605 if self .default_rst is not None :
@@ -489,7 +609,7 @@ def create_missing_domain(self, name):
489609
490610 gsr0 = Signal ()
491611 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
493613 # a simple reset synchronizer (with PUR as the asynchronous reset) does the job.
494614 m .submodules += [
495615 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):
512632 elif isinstance (buffer , io .FFBuffer ):
513633 result = FFBuffer (buffer .direction , buffer .port )
514634 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:
516641 else :
517642 raise TypeError (f"Unsupported buffer type { buffer !r} " ) # :nocov:
518643 if buffer .direction is not io .Direction .Output :
@@ -522,5 +647,5 @@ def get_io_buffer(self, buffer):
522647 result .oe = buffer .oe
523648 return result
524649
525- # CDC primitives are not currently specialized for ECP5 .
650+ # CDC primitives are not currently specialized for Lattice .
526651 # While Diamond supports false path constraints; nextpnr-ecp5 does not.
0 commit comments