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), at address 0x0
307 if fw_addr
is not None:
309 self
.bootmem
= SRAMPeripheral(size
=0x8000, data_width
=sram_width
,
311 if firmware
is not None:
312 with
open(firmware
, "rb") as f
:
313 words
= iter(lambda: f
.read(sram_width
// 8), b
'')
314 bios
= [int.from_bytes(w
, "little") for w
in words
]
315 self
.bootmem
.init
= bios
316 self
._decoder
.add(self
.bootmem
.bus
, addr
=fw_addr
) # ROM at fw_addr
318 # System Configuration info
319 self
.syscon
= MicrowattSYSCON(sys_clk_freq
=clk_freq
,
320 has_uart
=(uart_pins
is not None))
321 self
._decoder
.add(self
.syscon
.bus
, addr
=0xc0000000) # at 0xc000_0000
324 # SRAM (read-writeable BRAM)
325 self
.ram
= SRAMPeripheral(size
=4096)
326 self
._decoder
.add(self
.ram
.bus
, addr
=0x8000000) # at 0x8000_0000
328 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
329 if uart_pins
is not None:
330 # sigh actual UART in microwatt is 8-bit
331 self
.uart
= UART16550(data_width
=8, pins
=uart_pins
,
333 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
334 cvtuartbus
= wishbone
.Interface(addr_width
=5, data_width
=32,
337 umap
= MemoryMap(addr_width
=7, data_width
=8, name
="uart_map")
338 cvtuartbus
.memory_map
= umap
339 self
._decoder
.add(cvtuartbus
, addr
=0xc0002000) # 16550 UART addr
340 self
.cvtuartbus
= cvtuartbus
342 # SDRAM module using opencores sdr_ctrl
344 class MT48LC16M16(SDRModule):
350 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
354 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
363 if ddr_pins
is not None or fpga
== 'sim':
364 ddrmodule
= dram_cls(clk_freq
, "1:2") # match DDR3 ASIC P/N
367 drs
= DomainRenamer("dramsync")
370 self
.ddrphy
= FakePHY(module
=ddrmodule
,
371 settings
=sim_ddr3_settings(clk_freq
),
372 verbosity
=SDRAM_VERBOSE_DBG
,
375 self
.ddrphy
= drs(ECP5DDRPHY(ddr_pins
, sys_clk_freq
=clk_freq
))
376 self
._decoder
.add(self
.ddrphy
.bus
, addr
=ddrphy_addr
)
378 dramcore
= gramCore(phy
=self
.ddrphy
,
379 geom_settings
=ddrmodule
.geom_settings
,
380 timing_settings
=ddrmodule
.timing_settings
,
383 self
.dramcore
= dramcore
385 self
.dramcore
= drs(dramcore
)
386 self
._decoder
.add(self
.dramcore
.bus
, addr
=dramcore_addr
)
388 # map the DRAM onto Wishbone, XXX use stall but set classic below
389 drambone
= gramWishbone(dramcore
, features
={'stall'})
391 self
.drambone
= drambone
393 self
.drambone
= drs(drambone
)
394 self
._decoder
.add(self
.drambone
.bus
, addr
=ddr_addr
)
397 if spi_0_pins
is not None and fpga
in ['sim',
399 'rcs_arctic_tern_bmc_card',
403 # The Lattice ECP5 devices require special handling on the
404 # dedicated SPI clock line, which is shared with the internal
405 # SPI controller used for FPGA bitstream loading.
406 spi0_is_lattice_ecp5_clk
= False
407 if fpga
in ['versa_ecp5',
409 'rcs_arctic_tern_bmc_card',
411 spi0_is_lattice_ecp5_clk
= True
413 # Tercel contains two independent Wishbone regions, a
414 # configuration region and the direct API access region,
415 # Set the SPI 0 access region to 16MB, as the FPGA
416 # bitstream Flash device is unlikely to be larger than this.
417 # The main SPI Flash (SPI 1) should be set to at
418 # least 28 bits (256MB) to allow the use of large 4BA devices.
419 self
.spi0
= Tercel(data_width
=32, spi_region_addr_width
=24,
420 adr_offset
=spi0_addr
,
424 lattice_ecp5_usrmclk
=spi0_is_lattice_ecp5_clk
)
425 self
._decoder
.add(self
.spi0
.bus
, addr
=spi0_addr
)
426 self
._decoder
.add(self
.spi0
.cfg_bus
, addr
=spi0_cfg_addr
)
429 if ethmac_0_pins
is not None and fpga
in ['versa_ecp5',
432 # The OpenCores Ethernet MAC contains two independent Wishbone
433 # interfaces, a slave (configuration) interface and a master (DMA)
435 self
.eth0
= EthMAC(pins
=ethmac_0_pins
)
436 self
._arbiter
.add(self
.eth0
.master_bus
)
437 self
._decoder
.add(self
.eth0
.slave_bus
, addr
=eth0_cfg_addr
)
438 self
.intc
.add_irq(self
.eth0
.irq
, index
=eth0_irqno
)
440 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
441 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
443 if hyperram_pins
is not None:
444 self
.hyperram
= HyperRAM(io
=hyperram_pins
, phy_kls
=HyperRAMPHY
,
446 latency
=7) # Winbond W956D8MBYA
447 self
._decoder
.add(self
.hyperram
.bus
, addr
=hyperram_addr
)
449 self
.memory_map
= self
._decoder
.bus
.memory_map
451 self
.clk_freq
= clk_freq
454 def elaborate(self
, platform
):
458 # add the peripherals and clock-reset-generator
459 if platform
is not None and hasattr(self
, "crg"):
460 m
.submodules
.sysclk
= self
.crg
462 if hasattr(self
, "bootmem"):
463 m
.submodules
.bootmem
= self
.bootmem
464 m
.submodules
.syscon
= self
.syscon
465 if hasattr(self
, "ram"):
466 m
.submodules
.ram
= self
.ram
467 if hasattr(self
, "uart"):
468 m
.submodules
.uart
= self
.uart
469 comb
+= self
.uart
.cts_i
.eq(1)
470 comb
+= self
.uart
.dsr_i
.eq(1)
471 comb
+= self
.uart
.ri_i
.eq(0)
472 comb
+= self
.uart
.dcd_i
.eq(1)
473 # sigh connect up the wishbone bus manually to deal with
474 # the mis-match on the data
475 uartbus
= self
.uart
.bus
476 comb
+= uartbus
.adr
.eq(self
.cvtuartbus
.adr
)
477 comb
+= uartbus
.stb
.eq(self
.cvtuartbus
.stb
)
478 comb
+= uartbus
.cyc
.eq(self
.cvtuartbus
.cyc
)
479 comb
+= uartbus
.sel
.eq(self
.cvtuartbus
.sel
)
480 comb
+= uartbus
.we
.eq(self
.cvtuartbus
.we
)
481 comb
+= uartbus
.dat_w
.eq(self
.cvtuartbus
.dat_w
) # drops 8..31
482 comb
+= self
.cvtuartbus
.dat_r
.eq(uartbus
.dat_r
) # drops 8..31
483 comb
+= self
.cvtuartbus
.ack
.eq(uartbus
.ack
)
484 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
485 comb
+= uartbus
.stall
.eq(uartbus
.cyc
& ~uartbus
.ack
)
486 comb
+= self
.cvtuartbus
.stall
.eq(uartbus
.stall
)
487 if hasattr(self
, "cpu"):
488 m
.submodules
.intc
= self
.intc
489 m
.submodules
.extcore
= self
.cpu
490 m
.submodules
.dbuscvt
= self
.dbusdowncvt
491 m
.submodules
.ibuscvt
= self
.ibusdowncvt
492 # create stall sigs, assume wishbone classic
493 #ibus, dbus = self.cvtibus, self.cvtdbus
494 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
495 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
497 m
.submodules
.arbiter
= self
._arbiter
498 m
.submodules
.decoder
= self
._decoder
499 if hasattr(self
, "ddrphy"):
500 m
.submodules
.ddrphy
= self
.ddrphy
501 m
.submodules
.dramcore
= self
.dramcore
502 m
.submodules
.drambone
= drambone
= self
.drambone
503 # grrr, same problem with drambone: not WB4-pipe compliant
504 comb
+= drambone
.bus
.stall
.eq(drambone
.bus
.cyc
& ~drambone
.bus
.ack
)
506 # add hyperram module
507 if hasattr(self
, "hyperram"):
508 m
.submodules
.hyperram
= hyperram
= self
.hyperram
509 # grrr, same problem with hyperram: not WB4-pipe compliant
510 comb
+= hyperram
.bus
.stall
.eq(hyperram
.bus
.cyc
& ~hyperram
.bus
.ack
)
511 # set 3 top CSn lines to zero for now
512 if self
.fpga
== 'arty_a7':
513 comb
+= hyperram
.phy
.rst_n
.eq(ResetSignal())
515 # add blinky lights so we know FPGA is alive
516 if platform
is not None:
517 m
.submodules
.blinky
= Blinky()
519 # connect the arbiter (of wishbone masters)
520 # to the decoder (addressing wishbone slaves)
521 comb
+= self
._arbiter
.bus
.connect(self
._decoder
.bus
)
523 if hasattr(self
, "cpu"):
524 # wire up the CPU interrupts
525 comb
+= self
.cpu
.irq
.eq(self
.intc
.ip
)
530 # add uart16550 verilog source. assumes a directory
531 # structure where ls2 has been checked out in a common
533 # git clone https://github.com/freecores/uart16550
534 opencores_16550
= "../../uart16550/rtl/verilog"
535 pth
= os
.path
.split(__file__
)[0]
536 pth
= os
.path
.join(pth
, opencores_16550
)
537 fname
= os
.path
.abspath(pth
)
539 self
.uart
.add_verilog_source(fname
, platform
)
541 if hasattr(self
, "spi0"):
543 m
.submodules
.spi0
= spi
= self
.spi0
544 # gonna drive me nuts, this.
545 comb
+= spi
.bus
.stall
.eq(spi
.bus
.cyc
& ~spi
.bus
.ack
)
546 comb
+= spi
.cfg_bus
.stall
.eq(spi
.cfg_bus
.cyc
& ~spi
.cfg_bus
.ack
)
548 # add Tercel verilog source. assumes a directory structure where
549 # microwatt has been checked out in a common subdirectory with:
550 # git clone https://git.libre-soc.org/git/microwatt.git tercel-qspi
551 # git checkout 882ace781e4
552 raptor_tercel
= "../../tercel-qspi/tercel"
553 pth
= os
.path
.split(__file__
)[0]
554 pth
= os
.path
.join(pth
, raptor_tercel
)
555 fname
= os
.path
.abspath(pth
)
557 self
.spi0
.add_verilog_source(fname
, platform
)
559 if hasattr(self
, "eth0"):
560 # add ethernet submodule
561 m
.submodules
.eth0
= ethmac
= self
.eth0
563 # add EthMAC verilog source. assumes a directory
564 # structure where the opencores ethmac has been checked out
565 # in a common subdirectory as:
566 # git clone https://github.com/freecores/ethmac
567 opencores_ethmac
= "../../ethmac/rtl/verilog"
568 pth
= os
.path
.split(__file__
)[0]
569 pth
= os
.path
.join(pth
, opencores_ethmac
)
570 fname
= os
.path
.abspath(pth
)
572 self
.eth0
.add_verilog_source(fname
, platform
)
575 pth
= os
.path
.split(__file__
)[0]
576 pth
= os
.path
.join(pth
, '../external_core_top.v')
577 fname
= os
.path
.abspath(pth
)
578 with
open(fname
) as f
:
579 platform
.add_file(fname
, f
)
584 # puzzlingly the only IO ports needed are peripheral pins,
585 # and at the moment that's just UART tx/rx.
587 ports
+= [self
.uart
.tx_o
, self
.uart
.rx_i
]
588 if hasattr(self
, "hyperram"):
589 ports
+= list(self
.hyperram
.ports())
590 if hasattr(self
, "ddrphy"):
591 if hasattr(self
.ddrphy
, "pads"): # real PHY
592 ports
+= list(self
.ddrphy
.pads
.fields
.values())
593 else: # FakePHY, get at the dfii pads, stops deletion of nets
594 for phase
in self
.dramcore
.dfii
.master
.phases
:
595 print ("dfi master", phase
)
596 ports
+= list(phase
.fields
.values())
597 for phase
in self
.dramcore
.dfii
.slave
.phases
:
598 print ("dfi master", phase
)
599 ports
+= list(phase
.fields
.values())
600 for phase
in self
.dramcore
.dfii
._inti
.phases
:
601 print ("dfi master", phase
)
602 ports
+= list(phase
.fields
.values())
603 ports
+= [ClockSignal(), ResetSignal()]
606 def build_platform(fpga
, firmware
):
608 # create a platform selected from the toolchain.
609 platform_kls
= {'versa_ecp5': VersaECP5Platform
,
610 'versa_ecp5_85': VersaECP5Platform85
,
611 'ulx3s': ULX3S_85F_Platform
,
612 'arty_a7': ArtyA7_100Platform
,
613 'isim': IcarusVersaPlatform
,
616 toolchain
= {'arty_a7': "yosys_nextpnr",
617 'versa_ecp5': 'Trellis',
618 'versa_ecp5_85': 'Trellis',
623 dram_cls
= {'arty_a7': None,
624 'versa_ecp5': MT41K64M16
,
625 'versa_ecp5_85': MT41K64M16
,
626 #'versa_ecp5': MT41K256M16,
631 if platform_kls
is not None:
632 platform
= platform_kls(toolchain
=toolchain
)
633 if fpga
== 'versa_ecp5_85':
634 platform
.speed
= "7" # HACK. speed grade 7, sigh
638 print ("platform", fpga
, firmware
, platform
)
640 # set clock frequency
645 clk_freq
= 50e6
# below 50 mhz, stops DRAM being enabled
646 if fpga
== 'versa_ecp5':
647 clk_freq
= 50e6
# crank right down to test hyperram
648 if fpga
== 'versa_ecp5_85':
650 if fpga
== 'arty_a7':
655 # select a firmware address
657 if firmware
is not None:
658 fw_addr
= 0x0000_0000
660 print ("fpga", fpga
, "firmware", firmware
)
662 # get UART resource pins
663 if platform
is not None:
664 uart_pins
= platform
.request("uart", 0)
666 uart_pins
= Record([('tx', 1), ('rx', 1)], name
="uart_0")
668 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
670 if (clk_freq
> 50e6
and platform
is not None and
671 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
672 ddr_pins
= platform
.request("ddr3", 0,
673 dir={"dq":"-", "dqs":"-"},
674 xdr
={"rst": 4, "clk":4, "a":4,
676 "odt":4, "ras":4, "cas":4, "we":4,
679 # Get SPI resource pins
681 if platform
is not None and \
682 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
683 # Override here to get FlashResource out of the way and enable Tercel
684 # direct access to the SPI flash
687 Subsignal("dq", Pins("W2 V2 Y2 W1", dir="io")),
688 Subsignal("cs_n", Pins("R2", dir="o")),
689 Attrs(PULLMODE
="NONE", DRIVE
="4", IO_TYPE
="LVCMOS33"))
691 platform
.add_resources(spi_0_ios
)
692 spi_0_pins
= platform
.request("spi_0", 0, dir={"dq":"io", "cs_n":"o"},
693 xdr
={"dq":1, "cs_n":0})
695 print ("spiflash pins", spi_0_pins
)
697 # Get Ethernet RMII resource pins
699 if False and platform
is not None and \
700 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
701 # Mainly on X3 connector, MDIO on X4 due to lack of pins
703 Resource("ethmac_0", 0,
704 Subsignal("mtx_clk", Pins("B19", dir="i")),
705 Subsignal("mtxd", Pins("B12 B9 E6 D6", dir="o")),
706 Subsignal("mtxen", Pins("E7", dir="o")),
707 Subsignal("mtxerr", Pins("D7", dir="o")),
708 Subsignal("mrx_clk", Pins("B11", dir="i")),
709 Subsignal("mrxd", Pins("B6 E9 D9 B8", dir="i")),
710 Subsignal("mrxdv", Pins("C8", dir="i")),
711 Subsignal("mrxerr", Pins("D8", dir="i")),
712 Subsignal("mcoll", Pins("E8", dir="i")),
713 Subsignal("mcrs", Pins("C7", dir="i")),
714 Subsignal("mdc", Pins("B18", dir="o")),
715 Subsignal("md", Pins("A18", dir="io")),
716 Attrs(PULLMODE
="NONE", DRIVE
="8", SLEWRATE
="FAST",
719 platform
.add_resources(ethmac_0_ios
)
720 ethmac_0_pins
= platform
.request("ethmac_0", 0,
721 dir={"mtx_clk":"i", "mtxd":"o",
723 "mtxerr":"o", "mrx_clk":"i",
725 "mrxdv":"i", "mrxerr":"i",
727 "mcrs":"i", "mdc":"o", "md":"io"},
728 xdr
={"mtx_clk": 0, "mtxd": 0,
730 "mtxerr": 0, "mrx_clk": 0,
732 "mrxdv": 0, "mrxerr": 0,
734 "mcrs": 0, "mdc": 0, "md": 0})
735 print ("ethmac pins", ethmac_0_pins
)
740 hyperram_pins
= HyperRAMPads()
741 elif fpga
in ['isim']:
742 hyperram_ios
= HyperRAMResource(0, cs_n
="V12 V14 U12 U14",
743 dq
="D4 D3 F4 F3 G2 H2 D2 E2",
744 rwds
="U13", rst_n
="T13", ck_p
="V10",
745 # ck_n="V11" - for later (DDR)
746 attrs
=Attrs(IOSTANDARD
="LVCMOS33"))
747 platform
.add_resources(hyperram_ios
)
748 hyperram_pins
= platform
.request("hyperram")
749 print ("isim a7 hyperram", hyperram_ios
)
750 # Digilent Arty A7-100t
751 elif platform
is not None and fpga
in ['arty_a7']:
752 hyperram_ios
= HyperRAMResource(0, cs_n
="V12 V14 U12 U14",
753 dq
="D4 D3 F4 F3 G2 H2 D2 E2",
754 rwds
="U13", rst_n
="T13", ck_p
="V10",
755 # ck_n="V11" - for later (DDR)
756 attrs
=Attrs(IOSTANDARD
="LVCMOS33"))
757 platform
.add_resources(hyperram_ios
)
758 hyperram_pins
= platform
.request("hyperram")
759 print ("arty a7 hyperram", hyperram_ios
)
761 elif platform
is not None and fpga
in ['versa_ecp5', 'versa_ecp5_85']:
762 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
763 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
764 rwds
="C14", rst_n
="E13", ck_p
="D13",
765 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
766 platform
.add_resources(hyperram_ios
)
767 hyperram_pins
= platform
.request("hyperram")
768 print ("versa ecp5 hyperram", hyperram_ios
)
769 print ("hyperram pins", hyperram_pins
)
772 soc
= DDR3SoC(fpga
=fpga
, dram_cls
=dram_cls
,
773 # check microwatt_soc.h for these
774 ddrphy_addr
=0xff000000, # DRAM_INIT_BASE firmware base
775 dramcore_addr
=0xc8000000, # DRAM_CTRL_BASE
776 ddr_addr
=0x40000000, # DRAM_BASE
777 spi0_addr
=0x10000000, # SPI0_BASE
778 spi0_cfg_addr
=0xc0003000, # SPI0_CTRL_BASE
779 eth0_cfg_addr
=0xc0004000, # ETH0_CTRL_BASE (4k)
780 eth0_irqno
=0, # ETH0_IRQ number
781 hyperram_addr
=0xa0000000, # HYPERRAM_BASE
786 spi_0_pins
=spi_0_pins
,
787 ethmac_0_pins
=ethmac_0_pins
,
788 hyperram_pins
=hyperram_pins
,
793 if toolchain
== 'Trellis':
794 # add -abc9 option to yosys synth_ecp5
795 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
796 #os.environ['NMIGEN_synth_opts'] = '-abc9'
797 os
.environ
['NMIGEN_synth_opts'] = '-nowidelut'
799 if platform
is not None:
800 # build and upload it
802 platform
.build(soc
, do_program
=False,
803 do_build
=True, build_dir
="build_simsoc")
805 platform
.build(soc
, do_program
=True)
807 # for now, generate verilog
808 vl
= verilog
.convert(soc
, ports
=soc
.ports())
809 with
open("ls2.v", "w") as f
:
813 # urrr this gets exec()d by the build process without arguments
814 # which screws up. use the arty_a7_ls2.py etc. with no arguments
815 if __name__
== '__main__':
818 if len(sys
.argv
) >= 2:
820 if len(sys
.argv
) >= 3:
821 firmware
= sys
.argv
[2]
822 build_platform(fpga
, firmware
)