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
55 from arty_crg
import ArtyA7CRG
60 def sim_ddr3_settings(clk_freq
=100e6
):
61 tck
= 2/(2*2*clk_freq
)
67 cl
, cwl
= get_cl_cw("DDR3", tck
)
68 cl_sys_latency
= get_sys_latency(nphases
, cl
)
69 cwl_sys_latency
= get_sys_latency(nphases
, cwl
)
70 rdcmdphase
, rdphase
= get_sys_phases(nphases
, cl_sys_latency
, cl
)
71 wrcmdphase
, wrphase
= get_sys_phases(nphases
, cwl_sys_latency
, cwl
)
76 dfi_databits
=4*databits
,
81 rdcmdphase
=rdcmdphase
,
82 wrcmdphase
=wrcmdphase
,
85 read_latency
=2 + cl_sys_latency
+ 2 + log2_int(4//nphases
) + 4,
86 write_latency
=cwl_sys_latency
90 class WB64to32Convert(Elaboratable
):
91 """Microwatt IO wishbone slave 64->32 bits converter
93 For timing reasons, this adds a one cycle latch on the way both
94 in and out. This relaxes timing and routing pressure on the "main"
95 memory bus by moving all simple IOs to a slower 32-bit bus.
97 This implementation is rather dumb at the moment, no stash buffer,
98 so we stall whenever that latch is busy. This can be improved.
100 def __init__(self
, master
, slave
):
104 def elaborate(self
, platform
):
106 comb
, sync
= m
.d
.comb
, m
.d
.sync
107 master
, slave
= self
.master
, self
.slave
114 with m
.State("IDLE"):
115 # Clear ACK (and has_top_r) in case it was set
116 sync
+= master
.ack
.eq(0)
117 sync
+= has_top_r
.eq(0)
119 # Do we have a cycle ?
120 with m
.If(master
.cyc
& master
.stb
):
121 # Stall master until we are done, we are't (yet) pipelining
122 # this, it's all slow IOs.
123 sync
+= master
.stall
.eq(1)
125 # Start cycle downstream
126 sync
+= slave
.cyc
.eq(1)
127 sync
+= slave
.stb
.eq(1)
129 # Do we have a top word and/or a bottom word ?
130 comb
+= has_top
.eq(master
.sel
[4:].bool())
131 comb
+= has_bot
.eq(master
.sel
[:4].bool())
132 # record the has_top flag for the next FSM state
133 sync
+= has_top_r
.eq(has_top
)
135 # Copy write enable to IO out, copy address as well,
136 # LSB is set later based on HI/LO
137 sync
+= slave
.we
.eq(master
.we
)
138 sync
+= slave
.adr
.eq(Cat(0, master
.adr
))
140 # If we have a bottom word, handle it first, otherwise
141 # send the top word down. XXX Split the actual mux out
142 # and only generate a control signal.
144 with m
.If(master
.we
):
145 sync
+= slave
.dat_w
.eq(master
.dat_w
[:32])
146 sync
+= slave
.sel
.eq(master
.sel
[:4])
148 # Wait for ack on BOTTOM half
149 m
.next
= "WAIT_ACK_BOT"
152 with m
.If(master
.we
):
153 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
154 sync
+= slave
.sel
.eq(master
.sel
[4:])
156 # Bump LSB of address
157 sync
+= slave
.adr
[0].eq(1)
159 # Wait for ack on TOP half
160 m
.next
= "WAIT_ACK_TOP"
163 with m
.State("WAIT_ACK_BOT"):
164 # If we aren't stalled by the device, clear stb
165 if hasattr(slave
, "stall"):
166 with m
.If(~slave
.stall
):
167 sync
+= slave
.stb
.eq(0)
170 with m
.If(slave
.ack
):
171 # If it's a read, latch the data
172 with m
.If(~slave
.we
):
173 sync
+= master
.dat_r
[:32].eq(slave
.dat_r
)
175 # Do we have a "top" part as well ?
176 with m
.If(has_top_r
):
178 with m
.If(master
.we
):
179 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
180 sync
+= slave
.sel
.eq(master
.sel
[4:])
182 # Bump address and set STB
183 sync
+= slave
.adr
[0].eq(1)
184 sync
+= slave
.stb
.eq(1)
187 m
.next
= "WAIT_ACK_TOP"
190 # We are done, ack up, clear cyc downstram
191 sync
+= slave
.cyc
.eq(0)
192 sync
+= slave
.stb
.eq(0)
194 # And ack & unstall upstream
195 sync
+= master
.ack
.eq(1)
196 if hasattr(master
, "stall"):
197 sync
+= master
.stall
.eq(0)
202 with m
.State("WAIT_ACK_TOP"):
203 # If we aren't stalled by the device, clear stb
204 if hasattr(slave
, "stall"):
205 with m
.If(~slave
.stall
):
206 sync
+= slave
.stb
.eq(0)
209 with m
.If(slave
.ack
):
210 # If it's a read, latch the data
211 with m
.If(~slave
.we
):
212 sync
+= master
.dat_r
[32:].eq(slave
.dat_r
)
214 # We are done, ack up, clear cyc downstram
215 sync
+= slave
.cyc
.eq(0)
216 sync
+= slave
.stb
.eq(0)
218 # And ack & unstall upstream
219 sync
+= master
.ack
.eq(1)
220 if hasattr(master
, "stall"):
221 sync
+= master
.stall
.eq(0)
229 class DDR3SoC(SoC
, Elaboratable
):
230 def __init__(self
, *,
233 uart_pins
, spi_0_pins
,
234 ddr_pins
, ddrphy_addr
, dramcore_addr
, ddr_addr
,
237 spi0_addr
, spi0_cfg_addr
,
243 # wishbone routing is as follows:
256 # +---decoder----+--------+---------+-------+
258 # uart XICS CSRs DRAM XIP SPI HyperRAM
260 # set up wishbone bus arbiter and decoder. arbiter routes,
261 # decoder maps local-relative addressed satellites to global addresses
262 self
._arbiter
= wishbone
.Arbiter(addr_width
=30, data_width
=32,
264 features
={"cti", "bte", "stall"})
265 self
._decoder
= wishbone
.Decoder(addr_width
=30, data_width
=32,
267 features
={"cti", "bte", "stall"})
269 # default firmware name
271 firmware
= "firmware/main.bin"
273 # set up clock request generator
274 if fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
275 self
.crg
= ECPIX5CRG(clk_freq
)
276 if fpga
in ['arty_a7']:
277 self
.crg
= ArtyA7CRG(clk_freq
)
279 # set up CPU, with 64-to-32-bit downconverters
281 self
.cpu
= ExternalCore(name
="ext_core")
282 cvtdbus
= wishbone
.Interface(addr_width
=30, data_width
=32,
283 granularity
=8, features
={'stall'})
284 cvtibus
= wishbone
.Interface(addr_width
=30, data_width
=32,
285 granularity
=8, features
={'stall'})
286 self
.dbusdowncvt
= WB64to32Convert(self
.cpu
.dbus
, cvtdbus
)
287 self
.ibusdowncvt
= WB64to32Convert(self
.cpu
.ibus
, cvtibus
)
288 self
._arbiter
.add(cvtibus
) # I-Cache Master
289 self
._arbiter
.add(cvtdbus
) # D-Cache Master. TODO JTAG master
290 self
.cvtibus
= cvtibus
291 self
.cvtdbus
= cvtdbus
293 # CPU interrupt controller
294 self
.intc
= GenericInterruptController(width
=len(self
.cpu
.irq
))
296 # SRAM (but actually a ROM, for firmware), at address 0x0
297 if fw_addr
is not None:
299 self
.bootmem
= SRAMPeripheral(size
=0x8000, data_width
=sram_width
,
301 if firmware
is not None:
302 with
open(firmware
, "rb") as f
:
303 words
= iter(lambda: f
.read(sram_width
// 8), b
'')
304 bios
= [int.from_bytes(w
, "little") for w
in words
]
305 self
.bootmem
.init
= bios
306 self
._decoder
.add(self
.bootmem
.bus
, addr
=fw_addr
) # ROM at fw_addr
308 # System Configuration info
309 self
.syscon
= MicrowattSYSCON(sys_clk_freq
=clk_freq
,
310 has_uart
=(uart_pins
is not None))
311 self
._decoder
.add(self
.syscon
.bus
, addr
=0xc0000000) # at 0xc000_0000
314 # SRAM (read-writeable BRAM)
315 self
.ram
= SRAMPeripheral(size
=4096)
316 self
._decoder
.add(self
.ram
.bus
, addr
=0x8000000) # at 0x8000_0000
318 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
319 if uart_pins
is not None:
320 # sigh actual UART in microwatt is 8-bit
321 self
.uart
= UART16550(data_width
=8, pins
=uart_pins
,
323 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
324 cvtuartbus
= wishbone
.Interface(addr_width
=5, data_width
=32,
327 umap
= MemoryMap(addr_width
=7, data_width
=8, name
="uart_map")
328 cvtuartbus
.memory_map
= umap
329 self
._decoder
.add(cvtuartbus
, addr
=0xc0002000) # 16550 UART addr
330 self
.cvtuartbus
= cvtuartbus
332 # SDRAM module using opencores sdr_ctrl
334 class MT48LC16M16(SDRModule):
340 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
344 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
353 if ddr_pins
is not None or fpga
== 'sim':
354 ddrmodule
= dram_cls(clk_freq
, "1:2") # match DDR3 ASIC P/N
357 drs
= DomainRenamer("dramsync")
360 self
.ddrphy
= FakePHY(module
=ddrmodule
,
361 settings
=sim_ddr3_settings(clk_freq
),
362 verbosity
=SDRAM_VERBOSE_DBG
,
365 self
.ddrphy
= drs(ECP5DDRPHY(ddr_pins
, sys_clk_freq
=clk_freq
))
366 self
._decoder
.add(self
.ddrphy
.bus
, addr
=ddrphy_addr
)
368 dramcore
= gramCore(phy
=self
.ddrphy
,
369 geom_settings
=ddrmodule
.geom_settings
,
370 timing_settings
=ddrmodule
.timing_settings
,
373 self
.dramcore
= dramcore
375 self
.dramcore
= drs(dramcore
)
376 self
._decoder
.add(self
.dramcore
.bus
, addr
=dramcore_addr
)
378 # map the DRAM onto Wishbone, XXX use stall but set classic below
379 drambone
= gramWishbone(dramcore
, features
={'stall'})
381 self
.drambone
= drambone
383 self
.drambone
= drs(drambone
)
384 self
._decoder
.add(self
.drambone
.bus
, addr
=ddr_addr
)
387 if spi_0_pins
is not None and fpga
in ['sim',
388 'rcs_arctic_tern_bmc_card']:
389 # The Lattice ECP5 devices require special handling on the
390 # dedicated SPI clock line, which is shared with the internal
391 # SPI controller used for FPGA bitstream loading.
392 spi0_is_lattice_ecp5_clk
= False
393 if platform
is not None and fpga
in ['versa_ecp5',
394 'rcs_arctic_tern_bmc_card',
396 spi0_is_lattice_ecp5_clk
= True
398 # Tercel contains two independent Wishbone regions, a
399 # configuration region and the direct API access region,
400 # Set the SPI 0 access region to 16MB, as the FPGA
401 # bitstream Flash device is unlikely to be larger than this.
402 # The main SPI Flash (SPI 1) should be set to at
403 # least 28 bits (256MB) to allow the use of large 4BA devices.
404 self
.spi0
= Tercel(data_width
=32, spi_region_addr_width
=24,
407 lattice_ecp5_usrmclk
=spi0_is_lattice_ecp5_clk
)
408 self
._decoder
.add(self
.spi0
.bus
, addr
=spi0_addr
)
409 self
._decoder
.add(self
.spi0
.cfg_bus
, addr
=spi0_cfg_addr
)
411 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
412 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
414 if hyperram_pins
is not None:
415 self
.hyperram
= HyperRAM(io
=hyperram_pins
, phy_kls
=HyperRAMPHY
,
417 self
._decoder
.add(self
.hyperram
.bus
, addr
=hyperram_addr
)
419 self
.memory_map
= self
._decoder
.bus
.memory_map
421 self
.clk_freq
= clk_freq
423 def elaborate(self
, platform
):
427 # add the peripherals and clock-reset-generator
428 if platform
is not None and hasattr(self
, "crg"):
429 m
.submodules
.sysclk
= self
.crg
431 if hasattr(self
, "bootmem"):
432 m
.submodules
.bootmem
= self
.bootmem
433 m
.submodules
.syscon
= self
.syscon
434 if hasattr(self
, "ram"):
435 m
.submodules
.ram
= self
.ram
436 if hasattr(self
, "uart"):
437 m
.submodules
.uart
= self
.uart
438 comb
+= self
.uart
.cts_i
.eq(1)
439 comb
+= self
.uart
.dsr_i
.eq(1)
440 comb
+= self
.uart
.ri_i
.eq(0)
441 comb
+= self
.uart
.dcd_i
.eq(1)
442 # sigh connect up the wishbone bus manually to deal with
443 # the mis-match on the data
444 uartbus
= self
.uart
.bus
445 comb
+= uartbus
.adr
.eq(self
.cvtuartbus
.adr
)
446 comb
+= uartbus
.stb
.eq(self
.cvtuartbus
.stb
)
447 comb
+= uartbus
.cyc
.eq(self
.cvtuartbus
.cyc
)
448 comb
+= uartbus
.sel
.eq(self
.cvtuartbus
.sel
)
449 comb
+= uartbus
.we
.eq(self
.cvtuartbus
.we
)
450 comb
+= uartbus
.dat_w
.eq(self
.cvtuartbus
.dat_w
) # drops 8..31
451 comb
+= self
.cvtuartbus
.dat_r
.eq(uartbus
.dat_r
) # drops 8..31
452 comb
+= self
.cvtuartbus
.ack
.eq(uartbus
.ack
)
453 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
454 comb
+= uartbus
.stall
.eq(uartbus
.cyc
& ~uartbus
.ack
)
455 comb
+= self
.cvtuartbus
.stall
.eq(uartbus
.stall
)
456 if hasattr(self
, "cpu"):
457 m
.submodules
.intc
= self
.intc
458 m
.submodules
.extcore
= self
.cpu
459 m
.submodules
.dbuscvt
= self
.dbusdowncvt
460 m
.submodules
.ibuscvt
= self
.ibusdowncvt
461 # create stall sigs, assume wishbone classic
462 #ibus, dbus = self.cvtibus, self.cvtdbus
463 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
464 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
466 m
.submodules
.arbiter
= self
._arbiter
467 m
.submodules
.decoder
= self
._decoder
468 if hasattr(self
, "ddrphy"):
469 m
.submodules
.ddrphy
= self
.ddrphy
470 m
.submodules
.dramcore
= self
.dramcore
471 m
.submodules
.drambone
= drambone
= self
.drambone
472 # grrr, same problem with drambone: not WB4-pipe compliant
473 comb
+= drambone
.bus
.stall
.eq(drambone
.bus
.cyc
& ~drambone
.bus
.ack
)
475 # add hyperram module
476 if hasattr(self
, "hyperram"):
477 m
.submodules
.hyperram
= hyperram
= self
.hyperram
478 # grrr, same problem with hyperram: not WB4-pipe compliant
479 comb
+= hyperram
.bus
.stall
.eq(hyperram
.bus
.cyc
& ~hyperram
.bus
.ack
)
481 # add blinky lights so we know FPGA is alive
482 if platform
is not None:
483 m
.submodules
.blinky
= Blinky()
485 # connect the arbiter (of wishbone masters)
486 # to the decoder (addressing wishbone slaves)
487 comb
+= self
._arbiter
.bus
.connect(self
._decoder
.bus
)
489 if hasattr(self
, "cpu"):
490 # wire up the CPU interrupts
491 comb
+= self
.cpu
.irq
.eq(self
.intc
.ip
)
496 # add uart16550 verilog source. assumes a directory
497 # structure where ls2 has been checked out in a common
498 # subdirectory as https://github.com/freecores/uart16550
499 opencores_16550
= "../../uart16550/rtl/verilog"
500 pth
= os
.path
.split(__file__
)[0]
501 pth
= os
.path
.join(pth
, opencores_16550
)
502 fname
= os
.path
.abspath(pth
)
504 self
.uart
.add_verilog_source(fname
, platform
)
506 if hasattr(self
, "spi0"):
507 # add Tercel verilog source. assumes a directory
508 # structure where ls2 has been checked out in a common
509 # subdirectory as https://git.libre-soc.org/git/microwatt.git
510 raptor_tercel
= "../../microwatt/tercel"
511 pth
= os
.path
.split(__file__
)[0]
512 pth
= os
.path
.join(pth
, raptor_tercel
)
513 fname
= os
.path
.abspath(pth
)
515 self
.spi0
.add_verilog_source(fname
, platform
)
518 pth
= os
.path
.split(__file__
)[0]
519 pth
= os
.path
.join(pth
, '../external_core_top.v')
520 fname
= os
.path
.abspath(pth
)
521 with
open(fname
) as f
:
522 platform
.add_file(fname
, f
)
527 # puzzlingly the only IO ports needed are peripheral pins,
528 # and at the moment that's just UART tx/rx.
530 ports
+= [self
.uart
.tx_o
, self
.uart
.rx_i
]
531 if hasattr(self
, "hyperram"):
532 ports
+= list(self
.hyperram
.ports())
533 if hasattr(self
, "ddrphy"):
534 if hasattr(self
.ddrphy
, "pads"): # real PHY
535 ports
+= list(self
.ddrphy
.pads
.fields
.values())
536 else: # FakePHY, get at the dfii pads, stops deletion of nets
537 for phase
in self
.dramcore
.dfii
.master
.phases
:
538 print ("dfi master", phase
)
539 ports
+= list(phase
.fields
.values())
540 for phase
in self
.dramcore
.dfii
.slave
.phases
:
541 print ("dfi master", phase
)
542 ports
+= list(phase
.fields
.values())
543 for phase
in self
.dramcore
.dfii
._inti
.phases
:
544 print ("dfi master", phase
)
545 ports
+= list(phase
.fields
.values())
546 ports
+= [ClockSignal(), ResetSignal()]
549 def build_platform(fpga
, firmware
):
551 # create a platform selected from the toolchain.
552 platform_kls
= {'versa_ecp5': VersaECP5Platform
,
553 'versa_ecp5_85': VersaECP5Platform85
,
554 'ulx3s': ULX3S_85F_Platform
,
555 'arty_a7': ArtyA7_100Platform
,
556 'isim': IcarusVersaPlatform
,
559 toolchain
= {'arty_a7': "yosys_nextpnr",
560 'versa_ecp5': 'Trellis',
561 'versa_ecp5_85': 'Trellis',
566 dram_cls
= {'arty_a7': None,
567 'versa_ecp5': MT41K64M16
,
568 'versa_ecp5_85': MT41K64M16
,
569 #'versa_ecp5': MT41K256M16,
574 if platform_kls
is not None:
575 platform
= platform_kls(toolchain
=toolchain
)
576 if fpga
== 'versa_ecp5_85':
577 platform
.speed
= "7" # HACK. speed grade 7, sigh
581 # set clock frequency
585 if fpga
== 'versa_ecp5':
586 clk_freq
= 50e6
# crank right down to test hyperram
587 if fpga
== 'versa_ecp5_85':
589 if fpga
== 'arty_a7':
592 # select a firmware address
594 if firmware
is not None:
595 fw_addr
= 0x0000_0000
597 print ("fpga", fpga
, "firmware", firmware
)
599 # get UART resource pins
600 if platform
is not None:
601 uart_pins
= platform
.request("uart", 0)
603 uart_pins
= Record([('tx', 1), ('rx', 1)], name
="uart_0")
605 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
607 if (clk_freq
> 50e6
and platform
is not None and
608 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
609 ddr_pins
= platform
.request("ddr3", 0,
610 dir={"dq":"-", "dqs":"-"},
611 xdr
={"rst": 4, "clk":4, "a":4,
613 "odt":4, "ras":4, "cas":4, "we":4,
616 # Get SPI resource pins
618 if platform
is not None and fpga
in ['rcs_arctic_tern_bmc_card']:
619 if toolchain
== 'Trellis':
620 # The ECP5 series FPGAs handle the SPI clock directly on
621 # the FPGA configuration Flash device
622 spi_0_pins
= platform
.request("spi_0", 0,
623 dir={"dq":"io", "cs_n":"o"},
624 xdr
={"dq": 1, "cs_n": 1})
626 spi_0_pins
= platform
.request("spi_0", 0,
627 dir={"dq":"io", "cs_n":"o", "clk":"o"},
628 xdr
={"dq": 1, "cs_n": 1, "clk": 0})
632 if platform
is None or platform
in ['isim']:
633 hyperram_pins
= HyperRAMPads()
634 # Digilent Arty A7-100t
635 elif platform
is not None and fpga
in ['arty_a7']:
636 hyperram_ios
= HyperRAMResource(0, cs_n
="B11",
637 dq
="D4 D3 F4 F3 G2 H2 D2 E2",
638 rwds
="U13", rst_n
="T13", ck_p
="V10"
639 # ck_n="D12" - for later (DDR)
641 #attrs=Attrs(IO_TYPE="LVCMOS33"))
642 platform
.add_resources(hyperram_ios
)
643 hyperram_pins
= platform
.request("hyperram")
644 print ("arty a7 hyperram", hyperram_ios
)
646 elif platform
is not None and fpga
in ['versa_ecp5', 'versa_ecp5_85']:
647 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
648 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
649 rwds
="C14", rst_n
="E13", ck_p
="D13",
650 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
651 platform
.add_resources(hyperram_ios
)
652 hyperram_pins
= platform
.request("hyperram")
653 print ("versa ecp5 hyperram", hyperram_ios
)
656 soc
= DDR3SoC(fpga
=fpga
, dram_cls
=dram_cls
,
657 # check microwatt_soc.h for these
658 ddrphy_addr
=0xff000000, # DRAM_INIT_BASE firmware base
659 dramcore_addr
=0xc8000000, # DRAM_CTRL_BASE
660 ddr_addr
=0x40000000, # DRAM_BASE
661 spi0_addr
=0x10000000, # SPI0_BASE
662 spi0_cfg_addr
=0xc0003000, # SPI0_CTRL_BASE
663 hyperram_addr
=0xa0000000, # HYPERRAM_BASE
668 spi_0_pins
=spi_0_pins
,
669 hyperram_pins
=hyperram_pins
,
674 if toolchain
== 'Trellis':
675 # add -abc9 option to yosys synth_ecp5
676 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
677 #os.environ['NMIGEN_synth_opts'] = '-abc9'
678 os
.environ
['NMIGEN_synth_opts'] = '-nowidelut'
680 if platform
is not None:
681 # build and upload it
683 platform
.build(soc
, do_program
=False,
684 do_build
=True, build_dir
="build_simsoc")
686 platform
.build(soc
, do_program
=True)
688 # for now, generate verilog
689 vl
= verilog
.convert(soc
, ports
=soc
.ports())
690 with
open("ls2.v", "w") as f
:
694 # urrr this gets exec()d by the build process without arguments
695 # which screws up. use the arty_a7_ls2.py etc. with no arguments
696 if __name__
== '__main__':
699 if len(sys
.argv
) >= 2:
701 if len(sys
.argv
) >= 3:
702 firmware
= sys
.argv
[2]
703 build_platform(fpga
, firmware
)