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
.opencores_ethmac
import EthMAC
# OpenCores 10/100 Ethernet MAC
34 from soc
.bus
.external_core
import ExternalCore
# external libresoc/microwatt
35 from soc
.bus
.wb_downconvert
import WishboneDownConvert
36 from soc
.bus
.syscon
import MicrowattSYSCON
39 from gram
.common
import (PhySettings
, get_cl_cw
, get_sys_latency
,
41 from gram
.core
import gramCore
42 from gram
.phy
.ecp5ddrphy
import ECP5DDRPHY
43 from gram
.phy
.fakephy
import FakePHY
, SDRAM_VERBOSE_STD
, SDRAM_VERBOSE_DBG
44 from gram
.modules
import MT41K256M16
, MT41K64M16
45 from gram
.frontend
.wishbone
import gramWishbone
48 from nmigen
.build
import Resource
49 from nmigen
.build
import Subsignal
50 from nmigen
.build
import Pins
52 # Board (and simulation) platforms
53 from nmigen_boards
.versa_ecp5
import VersaECP5Platform
54 from nmigen_boards
.versa_ecp5
import VersaECP5Platform85
# custom board
55 from nmigen_boards
.ulx3s
import ULX3S_85F_Platform
56 from nmigen_boards
.arty_a7
import ArtyA7_100Platform
57 from nmigen_boards
.test
.blinky
import Blinky
58 from icarusversa
import IcarusVersaPlatform
59 # Clock-Reset Generator (works for all ECP5 platforms)
60 from ecp5_crg
import ECP5CRG
61 from arty_crg
import ArtyA7CRG
66 def sim_ddr3_settings(clk_freq
=100e6
):
67 tck
= 2/(2*2*clk_freq
)
73 cl
, cwl
= get_cl_cw("DDR3", tck
)
74 cl_sys_latency
= get_sys_latency(nphases
, cl
)
75 cwl_sys_latency
= get_sys_latency(nphases
, cwl
)
76 rdcmdphase
, rdphase
= get_sys_phases(nphases
, cl_sys_latency
, cl
)
77 wrcmdphase
, wrphase
= get_sys_phases(nphases
, cwl_sys_latency
, cwl
)
82 dfi_databits
=4*databits
,
87 rdcmdphase
=rdcmdphase
,
88 wrcmdphase
=wrcmdphase
,
91 read_latency
=2 + cl_sys_latency
+ 2 + log2_int(4//nphases
) + 4,
92 write_latency
=cwl_sys_latency
96 class WB64to32Convert(Elaboratable
):
97 """Microwatt IO wishbone slave 64->32 bits converter
99 For timing reasons, this adds a one cycle latch on the way both
100 in and out. This relaxes timing and routing pressure on the "main"
101 memory bus by moving all simple IOs to a slower 32-bit bus.
103 This implementation is rather dumb at the moment, no stash buffer,
104 so we stall whenever that latch is busy. This can be improved.
106 def __init__(self
, master
, slave
):
110 def elaborate(self
, platform
):
112 comb
, sync
= m
.d
.comb
, m
.d
.sync
113 master
, slave
= self
.master
, self
.slave
120 with m
.State("IDLE"):
121 # Clear ACK (and has_top_r) in case it was set
122 sync
+= master
.ack
.eq(0)
123 sync
+= has_top_r
.eq(0)
125 # Do we have a cycle ?
126 with m
.If(master
.cyc
& master
.stb
):
127 # Stall master until we are done, we are't (yet) pipelining
128 # this, it's all slow IOs.
129 sync
+= master
.stall
.eq(1)
131 # Start cycle downstream
132 sync
+= slave
.cyc
.eq(1)
133 sync
+= slave
.stb
.eq(1)
135 # Do we have a top word and/or a bottom word ?
136 comb
+= has_top
.eq(master
.sel
[4:].bool())
137 comb
+= has_bot
.eq(master
.sel
[:4].bool())
138 # record the has_top flag for the next FSM state
139 sync
+= has_top_r
.eq(has_top
)
141 # Copy write enable to IO out, copy address as well,
142 # LSB is set later based on HI/LO
143 sync
+= slave
.we
.eq(master
.we
)
144 sync
+= slave
.adr
.eq(Cat(0, master
.adr
))
146 # If we have a bottom word, handle it first, otherwise
147 # send the top word down. XXX Split the actual mux out
148 # and only generate a control signal.
150 with m
.If(master
.we
):
151 sync
+= slave
.dat_w
.eq(master
.dat_w
[:32])
152 sync
+= slave
.sel
.eq(master
.sel
[:4])
154 # Wait for ack on BOTTOM half
155 m
.next
= "WAIT_ACK_BOT"
158 with m
.If(master
.we
):
159 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
160 sync
+= slave
.sel
.eq(master
.sel
[4:])
162 # Bump LSB of address
163 sync
+= slave
.adr
[0].eq(1)
165 # Wait for ack on TOP half
166 m
.next
= "WAIT_ACK_TOP"
169 with m
.State("WAIT_ACK_BOT"):
170 # If we aren't stalled by the device, clear stb
171 if hasattr(slave
, "stall"):
172 with m
.If(~slave
.stall
):
173 sync
+= slave
.stb
.eq(0)
176 with m
.If(slave
.ack
):
177 # If it's a read, latch the data
178 with m
.If(~slave
.we
):
179 sync
+= master
.dat_r
[:32].eq(slave
.dat_r
)
181 # Do we have a "top" part as well ?
182 with m
.If(has_top_r
):
184 with m
.If(master
.we
):
185 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
186 sync
+= slave
.sel
.eq(master
.sel
[4:])
188 # Bump address and set STB
189 sync
+= slave
.adr
[0].eq(1)
190 sync
+= slave
.stb
.eq(1)
193 m
.next
= "WAIT_ACK_TOP"
196 # We are done, ack up, clear cyc downstram
197 sync
+= slave
.cyc
.eq(0)
198 sync
+= slave
.stb
.eq(0)
200 # And ack & unstall upstream
201 sync
+= master
.ack
.eq(1)
202 if hasattr(master
, "stall"):
203 sync
+= master
.stall
.eq(0)
208 with m
.State("WAIT_ACK_TOP"):
209 # If we aren't stalled by the device, clear stb
210 if hasattr(slave
, "stall"):
211 with m
.If(~slave
.stall
):
212 sync
+= slave
.stb
.eq(0)
215 with m
.If(slave
.ack
):
216 # If it's a read, latch the data
217 with m
.If(~slave
.we
):
218 sync
+= master
.dat_r
[32:].eq(slave
.dat_r
)
220 # We are done, ack up, clear cyc downstram
221 sync
+= slave
.cyc
.eq(0)
222 sync
+= slave
.stb
.eq(0)
224 # And ack & unstall upstream
225 sync
+= master
.ack
.eq(1)
226 if hasattr(master
, "stall"):
227 sync
+= master
.stall
.eq(0)
235 class DDR3SoC(SoC
, Elaboratable
):
236 def __init__(self
, *,
239 uart_pins
, spi_0_pins
, ethmac_0_pins
,
240 ddr_pins
, ddrphy_addr
, dramcore_addr
, ddr_addr
,
243 spi0_addr
, spi0_cfg_addr
,
244 eth0_cfg_addr
, eth0_irqno
,
250 # wishbone routing is as follows:
261 # arbiter------------------------------------------+
263 # +---decoder----+--------+---------+-------+--------+ |
265 # uart XICS CSRs DRAM XIP SPI HyperRAM EthMAC
267 # set up wishbone bus arbiter and decoder. arbiter routes,
268 # decoder maps local-relative addressed satellites to global addresses
269 self
._arbiter
= wishbone
.Arbiter(addr_width
=30, data_width
=32,
271 features
={"cti", "bte", "stall"})
272 self
._decoder
= wishbone
.Decoder(addr_width
=30, data_width
=32,
274 features
={"cti", "bte", "stall"})
276 # default firmware name
278 firmware
= "firmware/main.bin"
280 # set up clock request generator
282 if fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s']:
285 self
.crg
= ECP5CRG(clk_freq
, pod_bits
)
286 if fpga
in ['arty_a7']:
287 self
.crg
= ArtyA7CRG(clk_freq
)
289 # set up CPU, with 64-to-32-bit downconverters
291 self
.cpu
= ExternalCore(name
="ext_core")
292 cvtdbus
= wishbone
.Interface(addr_width
=30, data_width
=32,
293 granularity
=8, features
={'stall'})
294 cvtibus
= wishbone
.Interface(addr_width
=30, data_width
=32,
295 granularity
=8, features
={'stall'})
296 self
.dbusdowncvt
= WB64to32Convert(self
.cpu
.dbus
, cvtdbus
)
297 self
.ibusdowncvt
= WB64to32Convert(self
.cpu
.ibus
, cvtibus
)
298 self
._arbiter
.add(cvtibus
) # I-Cache Master
299 self
._arbiter
.add(cvtdbus
) # D-Cache Master. TODO JTAG master
300 self
.cvtibus
= cvtibus
301 self
.cvtdbus
= cvtdbus
303 # CPU interrupt controller
304 self
.intc
= GenericInterruptController(width
=len(self
.cpu
.irq
))
306 # SRAM (but actually a ROM, for firmware)
307 if fw_addr
is not None:
308 print ("fw at address %x" % fw_addr
)
310 self
.bootmem
= SRAMPeripheral(size
=0x8000, data_width
=sram_width
,
312 if firmware
is not None:
313 with
open(firmware
, "rb") as f
:
314 words
= iter(lambda: f
.read(sram_width
// 8), b
'')
315 bios
= [int.from_bytes(w
, "little") for w
in words
]
316 self
.bootmem
.init
= bios
317 self
._decoder
.add(self
.bootmem
.bus
, addr
=fw_addr
) # ROM at fw_addr
319 # System Configuration info
320 # offset executable ELF payload at 6 megabyte offset (2<<20)
321 spi_offset
= 2<<20 if (spi_0_pins
is not None) else None
322 dram_offset
= ddr_addr
if (ddr_pins
is not None) else None
323 self
.syscon
= MicrowattSYSCON(sys_clk_freq
=clk_freq
,
324 has_uart
=(uart_pins
is not None),
325 spi_offset
=spi_offset
,
326 dram_addr
=dram_offset
)
327 self
._decoder
.add(self
.syscon
.bus
, addr
=0xc0000000) # at 0xc000_0000
330 # SRAM (read-writeable BRAM)
331 self
.ram
= SRAMPeripheral(size
=4096)
332 self
._decoder
.add(self
.ram
.bus
, addr
=0x8000000) # at 0x8000_0000
334 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
335 if uart_pins
is not None:
336 # sigh actual UART in microwatt is 8-bit
337 self
.uart
= UART16550(data_width
=8, pins
=uart_pins
,
339 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
340 cvtuartbus
= wishbone
.Interface(addr_width
=5, data_width
=32,
343 umap
= MemoryMap(addr_width
=7, data_width
=8, name
="uart_map")
344 cvtuartbus
.memory_map
= umap
345 self
._decoder
.add(cvtuartbus
, addr
=0xc0002000) # 16550 UART addr
346 self
.cvtuartbus
= cvtuartbus
348 # SDRAM module using opencores sdr_ctrl
350 class MT48LC16M16(SDRModule):
356 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
360 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
369 if ddr_pins
is not None: # or fpga == 'sim':
370 ddrmodule
= dram_cls(clk_freq
, "1:2") # match DDR3 ASIC P/N
372 # remap both the sync domain (wherever it occurs) and
373 # the sync2x domain. technically this should NOT be done.
374 # it's a bit of a mess. ok: this should be done only
375 # when dramsync===sync (and dramsync2x===sync2x)
376 drs
= DomainRenamer({"sync": "dramsync",
377 "sync2x": "dramsync2x"})
379 # HOWEVER, when the ASyncBridge is deployed, the two domains
380 # must NOT be renamed, instead this used:
382 # and then the ASyncBridge takes care of it.
383 # but, back in ecp5_crg.py,
386 self
.ddrphy
= FakePHY(module
=ddrmodule
,
387 settings
=sim_ddr3_settings(clk_freq
),
388 verbosity
=SDRAM_VERBOSE_DBG
,
391 self
.ddrphy
= drs(ECP5DDRPHY(ddr_pins
, sys_clk_freq
=clk_freq
))
392 self
._decoder
.add(self
.ddrphy
.bus
, addr
=ddrphy_addr
)
394 dramcore
= gramCore(phy
=self
.ddrphy
,
395 geom_settings
=ddrmodule
.geom_settings
,
396 timing_settings
=ddrmodule
.timing_settings
,
399 self
.dramcore
= dramcore
401 self
.dramcore
= drs(dramcore
)
402 self
._decoder
.add(self
.dramcore
.bus
, addr
=dramcore_addr
)
404 # map the DRAM onto Wishbone, XXX use stall but set classic below
405 # XXX WHEN ADDING ASYNCBRIDGE IT IS THE **BRIDGE** THAT MUST
406 # XXX HAVE THE STALL SIGNAL, AND THE **BRIDGE** THAT MUST HAVE
407 # XXX stall=stb&~ack APPLIED
408 drambone
= gramWishbone(dramcore
, features
={'stall'})
410 self
.drambone
= drambone
412 self
.drambone
= drs(drambone
)
413 # XXX ADD THE ASYNCBRIDGE NOT THE DRAMBONE.BUS, THEN
414 # XXX ADD DRAMBONE.BUS TO ASYNCBRIDGE
415 self
._decoder
.add(self
.drambone
.bus
, addr
=ddr_addr
)
417 # additional SRAM at address if DRAM is not also at 0x0
418 # (TODO, check Flash, and HyperRAM as well)
419 if ddr_pins
is None or ddr_addr
!= 0x0:
420 print ("SRAM 0x8000 at address 0x0")
422 self
.sram
= SRAMPeripheral(size
=0x8000,
423 data_width
=sram_width
,
425 self
._decoder
.add(self
.sram
.bus
, addr
=0x0) # RAM at 0x0
428 if spi_0_pins
is not None and fpga
in ['sim',
430 'rcs_arctic_tern_bmc_card',
434 # The Lattice ECP5 devices require special handling on the
435 # dedicated SPI clock line, which is shared with the internal
436 # SPI controller used for FPGA bitstream loading.
437 spi0_is_lattice_ecp5_clk
= False
438 if fpga
in ['versa_ecp5',
440 'rcs_arctic_tern_bmc_card',
442 spi0_is_lattice_ecp5_clk
= True
444 # Tercel contains two independent Wishbone regions, a
445 # configuration region and the direct API access region,
446 # Set the SPI 0 access region to 16MB, as the FPGA
447 # bitstream Flash device is unlikely to be larger than this.
448 # The main SPI Flash (SPI 1) should be set to at
449 # least 28 bits (256MB) to allow the use of large 4BA devices.
450 self
.spi0
= Tercel(data_width
=32, spi_region_addr_width
=24,
451 adr_offset
=spi0_addr
,
455 lattice_ecp5_usrmclk
=spi0_is_lattice_ecp5_clk
)
456 self
._decoder
.add(self
.spi0
.bus
, addr
=spi0_addr
)
457 self
._decoder
.add(self
.spi0
.cfg_bus
, addr
=spi0_cfg_addr
)
460 if ethmac_0_pins
is not None and fpga
in ['versa_ecp5',
463 # The OpenCores Ethernet MAC contains two independent Wishbone
464 # interfaces, a slave (configuration) interface and a master (DMA)
466 self
.eth0
= EthMAC(pins
=ethmac_0_pins
)
467 self
._arbiter
.add(self
.eth0
.master_bus
)
468 self
._decoder
.add(self
.eth0
.slave_bus
, addr
=eth0_cfg_addr
)
469 self
.intc
.add_irq(self
.eth0
.irq
, index
=eth0_irqno
)
471 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
472 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
474 if hyperram_pins
is not None:
475 self
.hyperram
= HyperRAM(io
=hyperram_pins
, phy_kls
=HyperRAMPHY
,
477 latency
=7) # Winbond W956D8MBYA
478 self
._decoder
.add(self
.hyperram
.bus
, addr
=hyperram_addr
)
480 self
.memory_map
= self
._decoder
.bus
.memory_map
482 self
.clk_freq
= clk_freq
485 def elaborate(self
, platform
):
489 # add the peripherals and clock-reset-generator
490 if platform
is not None and hasattr(self
, "crg"):
491 m
.submodules
.sysclk
= self
.crg
493 if hasattr(self
, "sram"):
494 m
.submodules
.sram
= self
.sram
495 if hasattr(self
, "bootmem"):
496 m
.submodules
.bootmem
= self
.bootmem
497 m
.submodules
.syscon
= self
.syscon
498 if hasattr(self
, "ram"):
499 m
.submodules
.ram
= self
.ram
500 if hasattr(self
, "uart"):
501 m
.submodules
.uart
= self
.uart
502 comb
+= self
.uart
.cts_i
.eq(1)
503 comb
+= self
.uart
.dsr_i
.eq(1)
504 comb
+= self
.uart
.ri_i
.eq(0)
505 comb
+= self
.uart
.dcd_i
.eq(1)
506 # sigh connect up the wishbone bus manually to deal with
507 # the mis-match on the data
508 uartbus
= self
.uart
.bus
509 comb
+= uartbus
.adr
.eq(self
.cvtuartbus
.adr
)
510 comb
+= uartbus
.stb
.eq(self
.cvtuartbus
.stb
)
511 comb
+= uartbus
.cyc
.eq(self
.cvtuartbus
.cyc
)
512 comb
+= uartbus
.sel
.eq(self
.cvtuartbus
.sel
)
513 comb
+= uartbus
.we
.eq(self
.cvtuartbus
.we
)
514 comb
+= uartbus
.dat_w
.eq(self
.cvtuartbus
.dat_w
) # drops 8..31
515 comb
+= self
.cvtuartbus
.dat_r
.eq(uartbus
.dat_r
) # drops 8..31
516 comb
+= self
.cvtuartbus
.ack
.eq(uartbus
.ack
)
517 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
518 comb
+= uartbus
.stall
.eq(uartbus
.cyc
& ~uartbus
.ack
)
519 comb
+= self
.cvtuartbus
.stall
.eq(uartbus
.stall
)
520 if hasattr(self
, "cpu"):
521 m
.submodules
.intc
= self
.intc
522 m
.submodules
.extcore
= self
.cpu
523 m
.submodules
.dbuscvt
= self
.dbusdowncvt
524 m
.submodules
.ibuscvt
= self
.ibusdowncvt
525 # create stall sigs, assume wishbone classic
526 #ibus, dbus = self.cvtibus, self.cvtdbus
527 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
528 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
530 m
.submodules
.arbiter
= self
._arbiter
531 m
.submodules
.decoder
= self
._decoder
532 if hasattr(self
, "ddrphy"):
533 m
.submodules
.ddrphy
= self
.ddrphy
534 m
.submodules
.dramcore
= self
.dramcore
535 m
.submodules
.drambone
= drambone
= self
.drambone
536 # grrr, same problem with drambone: not WB4-pipe compliant
537 # XXX TAKE THIS OUT, REPLACE WITH ASYNCBRIDGE HAVING
538 # XXX asyncbridge.bus.stall.eq(asyncbridge.bus.cyc & ...)
539 comb
+= drambone
.bus
.stall
.eq(drambone
.bus
.cyc
& ~drambone
.bus
.ack
)
541 # add hyperram module
542 if hasattr(self
, "hyperram"):
543 m
.submodules
.hyperram
= hyperram
= self
.hyperram
544 # grrr, same problem with hyperram: not WB4-pipe compliant
545 comb
+= hyperram
.bus
.stall
.eq(hyperram
.bus
.cyc
& ~hyperram
.bus
.ack
)
546 # set 3 top CSn lines to zero for now
547 if self
.fpga
== 'arty_a7':
548 comb
+= hyperram
.phy
.rst_n
.eq(ResetSignal())
550 # add blinky lights so we know FPGA is alive
551 if platform
is not None:
552 m
.submodules
.blinky
= Blinky()
554 # connect the arbiter (of wishbone masters)
555 # to the decoder (addressing wishbone slaves)
556 comb
+= self
._arbiter
.bus
.connect(self
._decoder
.bus
)
558 if hasattr(self
, "cpu"):
559 # wire up the CPU interrupts
560 comb
+= self
.cpu
.irq
.eq(self
.intc
.ip
)
565 # add uart16550 verilog source. assumes a directory
566 # structure where ls2 has been checked out in a common
568 # git clone https://github.com/freecores/uart16550
569 opencores_16550
= "../../uart16550/rtl/verilog"
570 pth
= os
.path
.split(__file__
)[0]
571 pth
= os
.path
.join(pth
, opencores_16550
)
572 fname
= os
.path
.abspath(pth
)
574 self
.uart
.add_verilog_source(fname
, platform
)
576 if hasattr(self
, "spi0"):
578 m
.submodules
.spi0
= spi
= self
.spi0
579 # gonna drive me nuts, this.
580 comb
+= spi
.bus
.stall
.eq(spi
.bus
.cyc
& ~spi
.bus
.ack
)
581 comb
+= spi
.cfg_bus
.stall
.eq(spi
.cfg_bus
.cyc
& ~spi
.cfg_bus
.ack
)
583 # add Tercel verilog source. assumes a directory structure where
584 # microwatt has been checked out in a common subdirectory with:
585 # git clone https://git.libre-soc.org/git/microwatt.git tercel-qspi
586 # git checkout 882ace781e4
587 raptor_tercel
= "../../tercel-qspi/tercel"
588 pth
= os
.path
.split(__file__
)[0]
589 pth
= os
.path
.join(pth
, raptor_tercel
)
590 fname
= os
.path
.abspath(pth
)
592 self
.spi0
.add_verilog_source(fname
, platform
)
594 if hasattr(self
, "eth0"):
595 # add ethernet submodule
596 m
.submodules
.eth0
= ethmac
= self
.eth0
598 # add EthMAC verilog source. assumes a directory
599 # structure where the opencores ethmac has been checked out
600 # in a common subdirectory as:
601 # git clone https://github.com/freecores/ethmac
602 opencores_ethmac
= "../../ethmac/rtl/verilog"
603 pth
= os
.path
.split(__file__
)[0]
604 pth
= os
.path
.join(pth
, opencores_ethmac
)
605 fname
= os
.path
.abspath(pth
)
607 self
.eth0
.add_verilog_source(fname
, platform
)
610 pth
= os
.path
.split(__file__
)[0]
611 pth
= os
.path
.join(pth
, '../external_core_top.v')
612 fname
= os
.path
.abspath(pth
)
613 with
open(fname
) as f
:
614 platform
.add_file(fname
, f
)
619 # puzzlingly the only IO ports needed are peripheral pins,
620 # and at the moment that's just UART tx/rx.
622 ports
+= [self
.uart
.tx_o
, self
.uart
.rx_i
]
623 if hasattr(self
, "hyperram"):
624 ports
+= list(self
.hyperram
.ports())
625 if hasattr(self
, "ddrphy"):
626 if hasattr(self
.ddrphy
, "pads"): # real PHY
627 ports
+= list(self
.ddrphy
.pads
.fields
.values())
628 else: # FakePHY, get at the dfii pads, stops deletion of nets
629 for phase
in self
.dramcore
.dfii
.master
.phases
:
630 print ("dfi master", phase
)
631 ports
+= list(phase
.fields
.values())
632 for phase
in self
.dramcore
.dfii
.slave
.phases
:
633 print ("dfi master", phase
)
634 ports
+= list(phase
.fields
.values())
635 for phase
in self
.dramcore
.dfii
._inti
.phases
:
636 print ("dfi master", phase
)
637 ports
+= list(phase
.fields
.values())
638 ports
+= [ClockSignal(), ResetSignal()]
641 def build_platform(fpga
, firmware
):
643 # create a platform selected from the toolchain.
644 platform_kls
= {'versa_ecp5': VersaECP5Platform
,
645 'versa_ecp5_85': VersaECP5Platform85
,
646 'ulx3s': ULX3S_85F_Platform
,
647 'arty_a7': ArtyA7_100Platform
,
648 'isim': IcarusVersaPlatform
,
651 toolchain
= {'arty_a7': "yosys_nextpnr",
652 'versa_ecp5': 'Trellis',
653 'versa_ecp5_85': 'Trellis',
658 dram_cls
= {'arty_a7': None,
659 'versa_ecp5': MT41K64M16
,
660 'versa_ecp5_85': MT41K64M16
,
661 #'versa_ecp5': MT41K256M16,
666 if platform_kls
is not None:
667 platform
= platform_kls(toolchain
=toolchain
)
668 if fpga
== 'versa_ecp5_85':
669 platform
.speed
= "7" # HACK. speed grade 7, sigh
673 print ("platform", fpga
, firmware
, platform
)
675 # set clock frequency
680 clk_freq
= 55e6
# below 50 mhz, stops DRAM being enabled
681 if fpga
== 'versa_ecp5':
682 clk_freq
= 55e6
# crank right down to test hyperram
683 if fpga
== 'versa_ecp5_85':
684 # 50MHz works. 100MHz works. 55MHz does NOT work.
685 # Stick with multiples of 50MHz...
687 if fpga
== 'arty_a7':
692 # select a firmware address
694 if firmware
is not None:
695 fw_addr
= 0xff00_0000 # firmware at HI address, now
697 print ("fpga", fpga
, "firmware", firmware
)
699 # get UART resource pins
700 if platform
is not None:
701 uart_pins
= platform
.request("uart", 0)
703 uart_pins
= Record([('tx', 1), ('rx', 1)], name
="uart_0")
705 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
707 if (clk_freq
>= 50e6
and platform
is not None and
708 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
709 ddr_pins
= platform
.request("ddr3", 0,
710 dir={"dq":"-", "dqs":"-"},
711 xdr
={"rst": 1, "clk":4, "a":4,
713 "odt":4, "ras":4, "cas":4, "we":4,
716 # Get SPI resource pins
718 if platform
is not None and \
719 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
720 # Override here to get FlashResource out of the way and enable Tercel
721 # direct access to the SPI flash.
722 # each pin needs a separate direction control
725 Subsignal("dq0", Pins("W2", dir="io")),
726 Subsignal("dq1", Pins("V2", dir="io")),
727 Subsignal("dq2", Pins("Y2", dir="io")),
728 Subsignal("dq3", Pins("W1", dir="io")),
729 Subsignal("cs_n", Pins("R2", dir="o")),
730 Attrs(PULLMODE
="NONE", DRIVE
="4", IO_TYPE
="LVCMOS33"))
732 platform
.add_resources(spi_0_ios
)
733 spi_0_pins
= platform
.request("spi_0", 0, dir={"cs_n":"o"},
734 xdr
={"dq0":1, "dq1": 1,
738 if platform
is not None and \
740 # each pin needs a separate direction control
743 Subsignal("dq0", Pins("K17", dir="io")),
744 Subsignal("dq1", Pins("K18", dir="io")),
745 Subsignal("dq2", Pins("L14", dir="io")),
746 Subsignal("dq3", Pins("M14", dir="io")),
747 Subsignal("cs_n", Pins("L13", dir="o")),
748 Subsignal("clk", Pins("L16", dir="o")),
749 Attrs(PULLMODE
="NONE", DRIVE
="4", IO_TYPE
="LVCMOS33"))
751 platform
.add_resources(spi_0_ios
)
752 spi_0_pins
= platform
.request("spi_0", 0)
754 print ("spiflash pins", spi_0_pins
)
756 # Get Ethernet RMII resource pins
758 if False and platform
is not None and \
759 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
760 # Mainly on X3 connector, MDIO on X4 due to lack of pins
762 Resource("ethmac_0", 0,
763 Subsignal("mtx_clk", Pins("B19", dir="i")),
764 Subsignal("mtxd", Pins("B12 B9 E6 D6", dir="o")),
765 Subsignal("mtxen", Pins("E7", dir="o")),
766 Subsignal("mtxerr", Pins("D7", dir="o")),
767 Subsignal("mrx_clk", Pins("B11", dir="i")),
768 Subsignal("mrxd", Pins("B6 E9 D9 B8", dir="i")),
769 Subsignal("mrxdv", Pins("C8", dir="i")),
770 Subsignal("mrxerr", Pins("D8", dir="i")),
771 Subsignal("mcoll", Pins("E8", dir="i")),
772 Subsignal("mcrs", Pins("C7", dir="i")),
773 Subsignal("mdc", Pins("B18", dir="o")),
774 Subsignal("md", Pins("A18", dir="io")),
775 Attrs(PULLMODE
="NONE", DRIVE
="8", SLEWRATE
="FAST",
778 platform
.add_resources(ethmac_0_ios
)
779 ethmac_0_pins
= platform
.request("ethmac_0", 0,
780 dir={"mtx_clk":"i", "mtxd":"o",
782 "mtxerr":"o", "mrx_clk":"i",
784 "mrxdv":"i", "mrxerr":"i",
786 "mcrs":"i", "mdc":"o", "md":"io"},
787 xdr
={"mtx_clk": 0, "mtxd": 0,
789 "mtxerr": 0, "mrx_clk": 0,
791 "mrxdv": 0, "mrxerr": 0,
793 "mcrs": 0, "mdc": 0, "md": 0})
794 print ("ethmac pins", ethmac_0_pins
)
799 hyperram_pins
= HyperRAMPads()
800 elif fpga
in ['isim']:
801 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
802 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
803 rwds
="C14", rst_n
="E13", ck_p
="D13",
804 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
805 platform
.add_resources(hyperram_ios
)
806 hyperram_pins
= platform
.request("hyperram")
807 print ("isim a7 hyperram", hyperram_ios
)
808 # Digilent Arty A7-100t
809 elif platform
is not None and fpga
in ['arty_a7']:
810 hyperram_ios
= HyperRAMResource(0, cs_n
="V12 V14 U12 U14",
811 dq
="D4 D3 F4 F3 G2 H2 D2 E2",
812 rwds
="U13", rst_n
="T13", ck_p
="V10",
813 # ck_n="V11" - for later (DDR)
814 attrs
=Attrs(IOSTANDARD
="LVCMOS33"))
815 platform
.add_resources(hyperram_ios
)
816 hyperram_pins
= platform
.request("hyperram")
817 print ("arty a7 hyperram", hyperram_ios
)
819 elif False and platform
is not None and fpga
in \
820 ['versa_ecp5', 'versa_ecp5_85']:
821 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
822 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
823 rwds
="C14", rst_n
="E13", ck_p
="D13",
824 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
825 platform
.add_resources(hyperram_ios
)
826 hyperram_pins
= platform
.request("hyperram")
827 print ("versa ecp5 hyperram", hyperram_ios
)
828 print ("hyperram pins", hyperram_pins
)
831 soc
= DDR3SoC(fpga
=fpga
, dram_cls
=dram_cls
,
832 # check microwatt_soc.h for these
833 ddrphy_addr
=0xfff00000, # DRAM_INIT_BASE, PHY address
834 dramcore_addr
=0xc8000000, # DRAM_CTRL_BASE
835 ddr_addr
=0x00000000, # DRAM_BASE
836 spi0_addr
=0xf0000000, # SPI0_BASE
837 spi0_cfg_addr
=0xc0006000, # SPI0_CTRL_BASE
838 eth0_cfg_addr
=0xc0004000, # ETH0_CTRL_BASE (4k)
839 eth0_irqno
=0, # ETH0_IRQ number
840 hyperram_addr
=0xa0000000, # HYPERRAM_BASE
845 spi_0_pins
=spi_0_pins
,
846 ethmac_0_pins
=ethmac_0_pins
,
847 hyperram_pins
=hyperram_pins
,
852 if toolchain
== 'Trellis':
853 # add -abc9 option to yosys synth_ecp5
854 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
855 #os.environ['NMIGEN_synth_opts'] = '-abc9'
856 os
.environ
['NMIGEN_synth_opts'] = '-nowidelut'
858 if platform
is not None:
859 # build and upload it
861 platform
.build(soc
, do_program
=False,
862 do_build
=True, build_dir
="build_simsoc")
864 platform
.build(soc
, do_program
=True)
866 # for now, generate verilog
867 vl
= verilog
.convert(soc
, ports
=soc
.ports())
868 with
open("ls2.v", "w") as f
:
872 # urrr this gets exec()d by the build process without arguments
873 # which screws up. use the arty_a7_ls2.py etc. with no arguments
874 if __name__
== '__main__':
877 if len(sys
.argv
) >= 2:
879 if len(sys
.argv
) >= 3:
880 firmware
= sys
.argv
[2]
881 build_platform(fpga
, firmware
)