1 # Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
2 # Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Copyright (C) 2022 Raptor Engineering, LLC <support@raptorengineering.com>
5 # Based on code from LambaConcept, from the gram example which is BSD-2-License
6 # https://github.com/jeanthom/gram/tree/master/examples
8 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
9 # under EU Grants 871528 and 957073, under the LGPLv3+ License
11 from nmigen
import (Module
, Elaboratable
, DomainRenamer
, Record
,
12 Signal
, Cat
, Const
, ClockSignal
, ResetSignal
)
13 from nmigen
.build
.dsl
import Attrs
14 from nmigen
.cli
import verilog
15 from nmigen
.lib
.cdc
import ResetSynchronizer
16 from nmigen_soc
import wishbone
, memory
17 from nmigen_soc
.memory
import MemoryMap
18 from nmigen
.utils
import log2_int
20 from nmigen_stdio
.serial
import AsyncSerial
23 from nmigen_boards
.resources
.memory
import HyperRAMResource
24 from lambdasoc
.periph
.hyperram
import HyperRAM
, HyperRAMPads
, HyperRAMPHY
26 from lambdasoc
.periph
.intc
import GenericInterruptController
27 from lambdasoc
.periph
.sram
import SRAMPeripheral
28 from lambdasoc
.periph
.timer
import TimerPeripheral
29 from lambdasoc
.periph
import Peripheral
30 from lambdasoc
.soc
.base
import SoC
31 from soc
.bus
.uart_16550
import UART16550
# opencores 16550 uart
32 from soc
.bus
.tercel
import Tercel
# SPI XIP master
33 from soc
.bus
.external_core
import ExternalCore
# external libresoc/microwatt
34 from soc
.bus
.wb_downconvert
import WishboneDownConvert
35 from soc
.bus
.syscon
import MicrowattSYSCON
38 from gram
.common
import (PhySettings
, get_cl_cw
, get_sys_latency
,
40 from gram
.core
import gramCore
41 from gram
.phy
.ecp5ddrphy
import ECP5DDRPHY
42 from gram
.phy
.fakephy
import FakePHY
, SDRAM_VERBOSE_STD
, SDRAM_VERBOSE_DBG
43 from gram
.modules
import MT41K256M16
, MT41K64M16
44 from gram
.frontend
.wishbone
import gramWishbone
46 # Board (and simulation) platforms
47 from nmigen_boards
.versa_ecp5
import VersaECP5Platform
48 from nmigen_boards
.versa_ecp5
import VersaECP5Platform85
# custom board
49 from nmigen_boards
.ulx3s
import ULX3S_85F_Platform
50 from nmigen_boards
.arty_a7
import ArtyA7_100Platform
51 from nmigen_boards
.test
.blinky
import Blinky
52 from icarusversa
import IcarusVersaPlatform
53 # Clock-Reset Generator (works for all ECP5 platforms)
54 from crg
import ECPIX5CRG
59 def sim_ddr3_settings(clk_freq
=100e6
):
60 tck
= 2/(2*2*clk_freq
)
66 cl
, cwl
= get_cl_cw("DDR3", tck
)
67 cl_sys_latency
= get_sys_latency(nphases
, cl
)
68 cwl_sys_latency
= get_sys_latency(nphases
, cwl
)
69 rdcmdphase
, rdphase
= get_sys_phases(nphases
, cl_sys_latency
, cl
)
70 wrcmdphase
, wrphase
= get_sys_phases(nphases
, cwl_sys_latency
, cwl
)
75 dfi_databits
=4*databits
,
80 rdcmdphase
=rdcmdphase
,
81 wrcmdphase
=wrcmdphase
,
84 read_latency
=2 + cl_sys_latency
+ 2 + log2_int(4//nphases
) + 4,
85 write_latency
=cwl_sys_latency
89 class WB64to32Convert(Elaboratable
):
90 """Microwatt IO wishbone slave 64->32 bits converter
92 For timing reasons, this adds a one cycle latch on the way both
93 in and out. This relaxes timing and routing pressure on the "main"
94 memory bus by moving all simple IOs to a slower 32-bit bus.
96 This implementation is rather dumb at the moment, no stash buffer,
97 so we stall whenever that latch is busy. This can be improved.
99 def __init__(self
, master
, slave
):
103 def elaborate(self
, platform
):
105 comb
, sync
= m
.d
.comb
, m
.d
.sync
106 master
, slave
= self
.master
, self
.slave
113 with m
.State("IDLE"):
114 # Clear ACK (and has_top_r) in case it was set
115 sync
+= master
.ack
.eq(0)
116 sync
+= has_top_r
.eq(0)
118 # Do we have a cycle ?
119 with m
.If(master
.cyc
& master
.stb
):
120 # Stall master until we are done, we are't (yet) pipelining
121 # this, it's all slow IOs.
122 sync
+= master
.stall
.eq(1)
124 # Start cycle downstream
125 sync
+= slave
.cyc
.eq(1)
126 sync
+= slave
.stb
.eq(1)
128 # Do we have a top word and/or a bottom word ?
129 comb
+= has_top
.eq(master
.sel
[4:].bool())
130 comb
+= has_bot
.eq(master
.sel
[:4].bool())
131 # record the has_top flag for the next FSM state
132 sync
+= has_top_r
.eq(has_top
)
134 # Copy write enable to IO out, copy address as well,
135 # LSB is set later based on HI/LO
136 sync
+= slave
.we
.eq(master
.we
)
137 sync
+= slave
.adr
.eq(Cat(0, master
.adr
))
139 # If we have a bottom word, handle it first, otherwise
140 # send the top word down. XXX Split the actual mux out
141 # and only generate a control signal.
143 with m
.If(master
.we
):
144 sync
+= slave
.dat_w
.eq(master
.dat_w
[:32])
145 sync
+= slave
.sel
.eq(master
.sel
[:4])
147 # Wait for ack on BOTTOM half
148 m
.next
= "WAIT_ACK_BOT"
151 with m
.If(master
.we
):
152 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
153 sync
+= slave
.sel
.eq(master
.sel
[4:])
155 # Bump LSB of address
156 sync
+= slave
.adr
[0].eq(1)
158 # Wait for ack on TOP half
159 m
.next
= "WAIT_ACK_TOP"
162 with m
.State("WAIT_ACK_BOT"):
163 # If we aren't stalled by the device, clear stb
164 if hasattr(slave
, "stall"):
165 with m
.If(~slave
.stall
):
166 sync
+= slave
.stb
.eq(0)
169 with m
.If(slave
.ack
):
170 # If it's a read, latch the data
171 with m
.If(~slave
.we
):
172 sync
+= master
.dat_r
[:32].eq(slave
.dat_r
)
174 # Do we have a "top" part as well ?
175 with m
.If(has_top_r
):
177 with m
.If(master
.we
):
178 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
179 sync
+= slave
.sel
.eq(master
.sel
[4:])
181 # Bump address and set STB
182 sync
+= slave
.adr
[0].eq(1)
183 sync
+= slave
.stb
.eq(1)
186 m
.next
= "WAIT_ACK_TOP"
189 # We are done, ack up, clear cyc downstram
190 sync
+= slave
.cyc
.eq(0)
191 sync
+= slave
.stb
.eq(0)
193 # And ack & unstall upstream
194 sync
+= master
.ack
.eq(1)
195 if hasattr(master
, "stall"):
196 sync
+= master
.stall
.eq(0)
201 with m
.State("WAIT_ACK_TOP"):
202 # If we aren't stalled by the device, clear stb
203 if hasattr(slave
, "stall"):
204 with m
.If(~slave
.stall
):
205 sync
+= slave
.stb
.eq(0)
208 with m
.If(slave
.ack
):
209 # If it's a read, latch the data
210 with m
.If(~slave
.we
):
211 sync
+= master
.dat_r
[32:].eq(slave
.dat_r
)
213 # We are done, ack up, clear cyc downstram
214 sync
+= slave
.cyc
.eq(0)
215 sync
+= slave
.stb
.eq(0)
217 # And ack & unstall upstream
218 sync
+= master
.ack
.eq(1)
219 if hasattr(master
, "stall"):
220 sync
+= master
.stall
.eq(0)
228 class DDR3SoC(SoC
, Elaboratable
):
229 def __init__(self
, *,
232 uart_pins
, spi_0_pins
,
233 ddr_pins
, ddrphy_addr
, dramcore_addr
, ddr_addr
,
236 spi0_addr
, spi0_cfg_addr
,
242 # wishbone routing is as follows:
255 # +---decoder----+--------+---------+-------+
257 # uart XICS CSRs DRAM XIP SPI HyperRAM
259 # set up wishbone bus arbiter and decoder. arbiter routes,
260 # decoder maps local-relative addressed satellites to global addresses
261 self
._arbiter
= wishbone
.Arbiter(addr_width
=30, data_width
=32,
263 features
={"cti", "bte", "stall"})
264 self
._decoder
= wishbone
.Decoder(addr_width
=30, data_width
=32,
266 features
={"cti", "bte", "stall"})
268 # default firmware name
270 firmware
= "firmware/main.bin"
272 # set up clock request generator
273 if fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
274 self
.crg
= ECPIX5CRG(clk_freq
)
275 if fpga
in ['arty_a7']:
276 self
.crg
= ArtyCRG(clk_freq
)
278 # set up CPU, with 64-to-32-bit downconverters
280 self
.cpu
= ExternalCore(name
="ext_core")
281 cvtdbus
= wishbone
.Interface(addr_width
=30, data_width
=32,
282 granularity
=8, features
={'stall'})
283 cvtibus
= wishbone
.Interface(addr_width
=30, data_width
=32,
284 granularity
=8, features
={'stall'})
285 self
.dbusdowncvt
= WB64to32Convert(self
.cpu
.dbus
, cvtdbus
)
286 self
.ibusdowncvt
= WB64to32Convert(self
.cpu
.ibus
, cvtibus
)
287 self
._arbiter
.add(cvtibus
) # I-Cache Master
288 self
._arbiter
.add(cvtdbus
) # D-Cache Master. TODO JTAG master
289 self
.cvtibus
= cvtibus
290 self
.cvtdbus
= cvtdbus
292 # CPU interrupt controller
293 self
.intc
= GenericInterruptController(width
=len(self
.cpu
.irq
))
295 # SRAM (but actually a ROM, for firmware), at address 0x0
296 if fw_addr
is not None:
298 self
.bootmem
= SRAMPeripheral(size
=0x8000, data_width
=sram_width
,
300 if firmware
is not None:
301 with
open(firmware
, "rb") as f
:
302 words
= iter(lambda: f
.read(sram_width
// 8), b
'')
303 bios
= [int.from_bytes(w
, "little") for w
in words
]
304 self
.bootmem
.init
= bios
305 self
._decoder
.add(self
.bootmem
.bus
, addr
=fw_addr
) # ROM at fw_addr
307 # System Configuration info
308 self
.syscon
= MicrowattSYSCON(sys_clk_freq
=clk_freq
,
309 has_uart
=(uart_pins
is not None))
310 self
._decoder
.add(self
.syscon
.bus
, addr
=0xc0000000) # at 0xc000_0000
313 # SRAM (read-writeable BRAM)
314 self
.ram
= SRAMPeripheral(size
=4096)
315 self
._decoder
.add(self
.ram
.bus
, addr
=0x8000000) # at 0x8000_0000
317 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
318 if uart_pins
is not None:
319 # sigh actual UART in microwatt is 8-bit
320 self
.uart
= UART16550(data_width
=8, pins
=uart_pins
,
322 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
323 cvtuartbus
= wishbone
.Interface(addr_width
=5, data_width
=32,
326 umap
= MemoryMap(addr_width
=7, data_width
=8, name
="uart_map")
327 cvtuartbus
.memory_map
= umap
328 self
._decoder
.add(cvtuartbus
, addr
=0xc0002000) # 16550 UART addr
329 self
.cvtuartbus
= cvtuartbus
331 # SDRAM module using opencores sdr_ctrl
333 class MT48LC16M16(SDRModule):
339 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
343 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
352 if ddr_pins
is not None or fpga
== 'sim':
353 ddrmodule
= dram_cls(clk_freq
, "1:2") # match DDR3 ASIC P/N
356 drs
= DomainRenamer("dramsync")
359 self
.ddrphy
= FakePHY(module
=ddrmodule
,
360 settings
=sim_ddr3_settings(clk_freq
),
361 verbosity
=SDRAM_VERBOSE_DBG
,
364 self
.ddrphy
= drs(ECP5DDRPHY(ddr_pins
, sys_clk_freq
=clk_freq
))
365 self
._decoder
.add(self
.ddrphy
.bus
, addr
=ddrphy_addr
)
367 dramcore
= gramCore(phy
=self
.ddrphy
,
368 geom_settings
=ddrmodule
.geom_settings
,
369 timing_settings
=ddrmodule
.timing_settings
,
372 self
.dramcore
= dramcore
374 self
.dramcore
= drs(dramcore
)
375 self
._decoder
.add(self
.dramcore
.bus
, addr
=dramcore_addr
)
377 # map the DRAM onto Wishbone, XXX use stall but set classic below
378 drambone
= gramWishbone(dramcore
, features
={'stall'})
380 self
.drambone
= drambone
382 self
.drambone
= drs(drambone
)
383 self
._decoder
.add(self
.drambone
.bus
, addr
=ddr_addr
)
386 if spi_0_pins
is not None and fpga
in ['sim',
387 'rcs_arctic_tern_bmc_card']:
388 # The Lattice ECP5 devices require special handling on the
389 # dedicated SPI clock line, which is shared with the internal
390 # SPI controller used for FPGA bitstream loading.
391 spi0_is_lattice_ecp5_clk
= False
392 if platform
is not None and fpga
in ['versa_ecp5',
393 'rcs_arctic_tern_bmc_card',
395 spi0_is_lattice_ecp5_clk
= True
397 # Tercel contains two independent Wishbone regions, a
398 # configuration region and the direct API access region,
399 # Set the SPI 0 access region to 16MB, as the FPGA
400 # bitstream Flash device is unlikely to be larger than this.
401 # The main SPI Flash (SPI 1) should be set to at
402 # least 28 bits (256MB) to allow the use of large 4BA devices.
403 self
.spi0
= Tercel(data_width
=32, spi_region_addr_width
=24,
406 lattice_ecp5_usrmclk
=spi0_is_lattice_ecp5_clk
)
407 self
._decoder
.add(self
.spi0
.bus
, addr
=spi0_addr
)
408 self
._decoder
.add(self
.spi0
.cfg_bus
, addr
=spi0_cfg_addr
)
410 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
411 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
413 if hyperram_pins
is not None:
414 self
.hyperram
= HyperRAM(io
=hyperram_pins
, phy_kls
=HyperRAMPHY
,
416 self
._decoder
.add(self
.hyperram
.bus
, addr
=hyperram_addr
)
418 self
.memory_map
= self
._decoder
.bus
.memory_map
420 self
.clk_freq
= clk_freq
422 def elaborate(self
, platform
):
426 # add the peripherals and clock-reset-generator
427 if platform
is not None and hasattr(self
, "crg"):
428 m
.submodules
.sysclk
= self
.crg
430 if hasattr(self
, "bootmem"):
431 m
.submodules
.bootmem
= self
.bootmem
432 m
.submodules
.syscon
= self
.syscon
433 if hasattr(self
, "ram"):
434 m
.submodules
.ram
= self
.ram
435 if hasattr(self
, "uart"):
436 m
.submodules
.uart
= self
.uart
437 comb
+= self
.uart
.cts_i
.eq(1)
438 comb
+= self
.uart
.dsr_i
.eq(1)
439 comb
+= self
.uart
.ri_i
.eq(0)
440 comb
+= self
.uart
.dcd_i
.eq(1)
441 # sigh connect up the wishbone bus manually to deal with
442 # the mis-match on the data
443 uartbus
= self
.uart
.bus
444 comb
+= uartbus
.adr
.eq(self
.cvtuartbus
.adr
)
445 comb
+= uartbus
.stb
.eq(self
.cvtuartbus
.stb
)
446 comb
+= uartbus
.cyc
.eq(self
.cvtuartbus
.cyc
)
447 comb
+= uartbus
.sel
.eq(self
.cvtuartbus
.sel
)
448 comb
+= uartbus
.we
.eq(self
.cvtuartbus
.we
)
449 comb
+= uartbus
.dat_w
.eq(self
.cvtuartbus
.dat_w
) # drops 8..31
450 comb
+= self
.cvtuartbus
.dat_r
.eq(uartbus
.dat_r
) # drops 8..31
451 comb
+= self
.cvtuartbus
.ack
.eq(uartbus
.ack
)
452 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
453 comb
+= uartbus
.stall
.eq(uartbus
.cyc
& ~uartbus
.ack
)
454 comb
+= self
.cvtuartbus
.stall
.eq(uartbus
.stall
)
455 if hasattr(self
, "cpu"):
456 m
.submodules
.intc
= self
.intc
457 m
.submodules
.extcore
= self
.cpu
458 m
.submodules
.dbuscvt
= self
.dbusdowncvt
459 m
.submodules
.ibuscvt
= self
.ibusdowncvt
460 # create stall sigs, assume wishbone classic
461 #ibus, dbus = self.cvtibus, self.cvtdbus
462 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
463 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
465 m
.submodules
.arbiter
= self
._arbiter
466 m
.submodules
.decoder
= self
._decoder
467 if hasattr(self
, "ddrphy"):
468 m
.submodules
.ddrphy
= self
.ddrphy
469 m
.submodules
.dramcore
= self
.dramcore
470 m
.submodules
.drambone
= drambone
= self
.drambone
471 # grrr, same problem with drambone: not WB4-pipe compliant
472 comb
+= drambone
.bus
.stall
.eq(drambone
.bus
.cyc
& ~drambone
.bus
.ack
)
474 # add hyperram module
475 if hasattr(self
, "hyperram"):
476 m
.submodules
.hyperram
= hyperram
= self
.hyperram
477 # grrr, same problem with hyperram: not WB4-pipe compliant
478 comb
+= hyperram
.bus
.stall
.eq(hyperram
.bus
.cyc
& ~hyperram
.bus
.ack
)
480 # add blinky lights so we know FPGA is alive
481 if platform
is not None:
482 m
.submodules
.blinky
= Blinky()
484 # connect the arbiter (of wishbone masters)
485 # to the decoder (addressing wishbone slaves)
486 comb
+= self
._arbiter
.bus
.connect(self
._decoder
.bus
)
488 if hasattr(self
, "cpu"):
489 # wire up the CPU interrupts
490 comb
+= self
.cpu
.irq
.eq(self
.intc
.ip
)
495 # add uart16550 verilog source. assumes a directory
496 # structure where ls2 has been checked out in a common
497 # subdirectory as https://github.com/freecores/uart16550
498 opencores_16550
= "../../uart16550/rtl/verilog"
499 pth
= os
.path
.split(__file__
)[0]
500 pth
= os
.path
.join(pth
, opencores_16550
)
501 fname
= os
.path
.abspath(pth
)
503 self
.uart
.add_verilog_source(fname
, platform
)
505 if hasattr(self
, "spi0"):
506 # add Tercel verilog source. assumes a directory
507 # structure where ls2 has been checked out in a common
508 # subdirectory as https://git.libre-soc.org/git/microwatt.git
509 raptor_tercel
= "../../microwatt/tercel"
510 pth
= os
.path
.split(__file__
)[0]
511 pth
= os
.path
.join(pth
, raptor_tercel
)
512 fname
= os
.path
.abspath(pth
)
514 self
.spi0
.add_verilog_source(fname
, platform
)
517 pth
= os
.path
.split(__file__
)[0]
518 pth
= os
.path
.join(pth
, '../external_core_top.v')
519 fname
= os
.path
.abspath(pth
)
520 with
open(fname
) as f
:
521 platform
.add_file(fname
, f
)
526 # puzzlingly the only IO ports needed are peripheral pins,
527 # and at the moment that's just UART tx/rx.
529 ports
+= [self
.uart
.tx_o
, self
.uart
.rx_i
]
530 if hasattr(self
, "hyperram"):
531 ports
+= list(self
.hyperram
.ports())
532 if hasattr(self
, "ddrphy"):
533 if hasattr(self
.ddrphy
, "pads"): # real PHY
534 ports
+= list(self
.ddrphy
.pads
.fields
.values())
535 else: # FakePHY, get at the dfii pads, stops deletion of nets
536 for phase
in self
.dramcore
.dfii
.master
.phases
:
537 print ("dfi master", phase
)
538 ports
+= list(phase
.fields
.values())
539 for phase
in self
.dramcore
.dfii
.slave
.phases
:
540 print ("dfi master", phase
)
541 ports
+= list(phase
.fields
.values())
542 for phase
in self
.dramcore
.dfii
._inti
.phases
:
543 print ("dfi master", phase
)
544 ports
+= list(phase
.fields
.values())
545 ports
+= [ClockSignal(), ResetSignal()]
548 if __name__
== "__main__":
550 # create a platform selected from the toolchain. defaults to VERSA_ECP5
551 # only VERSA_ECP5 will work for now because of the DDR3 module
553 if len(sys
.argv
) >= 2:
555 platform_kls
= {'versa_ecp5': VersaECP5Platform
,
556 'versa_ecp5_85': VersaECP5Platform85
,
557 'ulx3s': ULX3S_85F_Platform
,
558 'arty_a7': ArtyA7_100Platform
,
559 'isim': IcarusVersaPlatform
,
562 toolchain
= {'arty_a7': "yosys_nextpnr",
563 'versa_ecp5': 'Trellis',
564 'versa_ecp5_85': 'Trellis',
569 dram_cls
= {'arty_a7': None,
570 'versa_ecp5': MT41K64M16
,
571 'versa_ecp5_85': MT41K64M16
,
572 #'versa_ecp5': MT41K256M16,
577 if platform_kls
is not None:
578 platform
= platform_kls(toolchain
=toolchain
)
582 # set clock frequency
586 if fpga
== 'versa_ecp5':
587 clk_freq
= 50e6
# crank right down to test hyperram
588 if fpga
== 'versa_ecp5_85':
590 if fpga
== 'arty_a7':
593 # select a firmware file
596 if len(sys
.argv
) >= 3:
597 firmware
= sys
.argv
[2]
598 fw_addr
= 0x0000_0000
600 # get UART resource pins
601 if platform
is not None:
602 uart_pins
= platform
.request("uart", 0)
604 uart_pins
= Record([('tx', 1), ('rx', 1)], name
="uart_0")
606 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
608 if (clk_freq
> 50e6
and platform
is not None and
609 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
610 ddr_pins
= platform
.request("ddr3", 0,
611 dir={"dq":"-", "dqs":"-"},
612 xdr
={"rst": 4, "clk":4, "a":4,
614 "odt":4, "ras":4, "cas":4, "we":4,
617 # Get SPI resource pins
619 if platform
is not None and fpga
in ['rcs_arctic_tern_bmc_card']:
620 if toolchain
== 'Trellis':
621 # The ECP5 series FPGAs handle the SPI clock directly on
622 # the FPGA configuration Flash device
623 spi_0_pins
= platform
.request("spi_0", 0,
624 dir={"dq":"io", "cs_n":"o"},
625 xdr
={"dq": 1, "cs_n": 1})
627 spi_0_pins
= platform
.request("spi_0", 0,
628 dir={"dq":"io", "cs_n":"o", "clk":"o"},
629 xdr
={"dq": 1, "cs_n": 1, "clk": 0})
633 if platform
is not None and fpga
in ['versa_ecp5', 'versa_ecp5_85']:
634 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
635 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
636 rwds
="C14", rst_n
="E13", ck_p
="D13",
637 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
638 platform
.add_resources(hyperram_ios
)
639 hyperram_pins
= platform
.request("hyperram")
641 hyperram_pins
= HyperRAMPads()
644 soc
= DDR3SoC(fpga
=fpga
, dram_cls
=dram_cls
,
645 # check microwatt_soc.h for these
646 ddrphy_addr
=0xff000000, # DRAM_INIT_BASE firmware base
647 dramcore_addr
=0xc8000000, # DRAM_CTRL_BASE
648 ddr_addr
=0x40000000, # DRAM_BASE
649 spi0_addr
=0x10000000, # SPI0_BASE
650 spi0_cfg_addr
=0xc0003000, # SPI0_CTRL_BASE
651 hyperram_addr
=0xa0000000, # HYPERRAM_BASE
656 spi_0_pins
=spi_0_pins
,
657 hyperram_pins
=hyperram_pins
,
662 if toolchain
== 'Trellis':
663 # add -abc9 option to yosys synth_ecp5
664 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
665 #os.environ['NMIGEN_synth_opts'] = '-abc9'
666 os
.environ
['NMIGEN_synth_opts'] = '-nowidelut'
668 if platform
is not None:
669 # build and upload it
671 platform
.build(soc
, do_program
=False,
672 do_build
=True, build_dir
="build_simsoc")
674 platform
.build(soc
, do_program
=True)
676 # for now, generate verilog
677 vl
= verilog
.convert(soc
, ports
=soc
.ports())
678 with
open("ls2.v", "w") as f
: