nmigen: migrated

This commit is contained in:
occheung 2020-08-07 13:39:43 +08:00
parent 7351a9d58a
commit 8f1237a0a6
5 changed files with 331 additions and 0 deletions

BIN
nmigen/FPGA_pins.xlsx Normal file

Binary file not shown.

24
nmigen/fpga_config.py Normal file
View File

@ -0,0 +1,24 @@
# If the design does not create a "sync" clock domain, it is created by the nMigen build system
# using the platform default clock (and default reset, if any).
from nmigen import *
from humpback import *
class SimpleBlink(Elaboratable):
def elaborate(self, platform):
led = platform.request("user_led", 0)
counter = Signal(24)
pin = platform.request("gpioa", 0)
m = Module()
m.d.sync += counter.eq(counter + 1)
m.d.comb += led.o.eq(counter[23])
m.d.comb += pin.o.eq(led)
return m
if __name__ == "__main__":
platform = HumpbackPlatform()
platform.build(SimpleBlink(), do_program=False)

112
nmigen/humpback.py Normal file
View File

@ -0,0 +1,112 @@
# Strongly inspired by the migen build of humpback
# Using STM32 Nucleo-H743ZI2 board
# Note to self: Pin assignment differs from Nucleo-H743ZI
import os
import subprocess
from nmigen.build import *
from nmigen.vendor.lattice_ice40 import *
from nmigen_boards.resources import *
from resources import *
from pin_mapper import *
__all__ = ["HumpbackPlatform"]
class HumpbackPlatform(LatticeICE40Platform):
device = "iCE40HX8K" # Using ICE40HX8K-CT256
package = "CT256"
default_clk = "clk25" # Point of deviation: Clock speed for humpback is fixed at 25MHz
# Acquire GPIO mappings from gpio_mapper
gpio_dict, global_gpio_dict = GPIOMapping()
resources = [
# Define clock
Resource("clk25", 0, Pins("K9", dir="i"),
Clock(25e6), Attrs(GLOBAL=True, IO_STANDARD="SB_LVCMOS")
),
# Define user LED
Resource("user_led", 0, Pins("H3", dir="o"),
Attrs(IO_STANDARD="SB_LVCMOS")
),
# Serial interfaces: Only including pins usable to STM32
# Define UART interfaces
# UART interface: There are no pull ups from humpback.
# Need to configure STM32 pins to pull up.
# Note: Use USART in asynchronous mode
UARTResource(0,
rx="T11", tx="M13", rts="M15", cts="T10",
attrs=Attrs(IO_STANDARD="SB_LVCMOS", PULLUP=1)
),
# UART1 interface: Read note for UART interface above
# UART1 interface is broken due to pin rearrangement introduced for Nucleo-H743ZI2
# Uncomment if fixed, or found an alternative (e.g. bit banging UART)
# *UARTResource(1,
# tx="M11", rx="T13", rts="A6", cts="B16",
# attrs=Attrs(IO_STANDARD="SB_LVCMOS", PULLUP=1)
# ),
# Define SPI interfaces
# Note: Use "role=device" to make humpback a SPI slave
# The ~CS pin is a global pin, but not being configured global.
SPIResource(0,
cs="R2", clk="C8", mosi="N5", miso="T2",
attrs=Attrs(IO_STANDARD="SB_LVCMOS")
),
# Define I2C interface
# Note: Need to program pull up in stm32
# Use "role=device" to make humpback a I2C slave
I2CResource(0,
sda="T16", scl="M12",
attrs=Attrs(IO_STANDARD="SB_LVCMOS", PULLUP=1)
),
# TODO:STM32 GPIO pins, ignore other unusable pins as well
*GPIOResources(
pins_dict = gpio_dict, dir = "o",
attrs=Attrs(IO_STANDARD="SB_LVCMOS"),
),
*GPIOResources(
pins_dict = global_gpio_dict, dir = "o",
attrs=Attrs(GLOBAL=True, IO_STANDARD="SB_LVCMOS"),
),
]
connectors = []
# tool chain setup, using default ICE40 HX8K evaluation code
def toolchain_program(self, products, name):
iceprog = os.environ.get("ICEPROG", "iceprog")
with products.extract("{}.bin".format(name)) as bitstream_filename:
# TODO: this should be factored out and made customizable
subprocess.check_call([iceprog, "-S", bitstream_filename])
if __name__ == "__main__":
from nmigen_boards.test.blinky import *
HumpbackPlatform().build(Blinky(), do_program=False)

105
nmigen/pin_mapper.py Normal file
View File

@ -0,0 +1,105 @@
import pandas as pd
# Get dictionaries of:
# - Non-global GPIO to FPGA mapping
# - Global GPIO to FPGA mapping
# (Ordered)
def GPIOMapping():
# Standard extraction of data from excel file
data = pd.read_excel("nmigen/FPGA_pins.xlsx", skiprows = range(1,2))
df = pd.DataFrame(data, columns=["Designator", "Pin Name", "Net"])
stm32 = df[df.Net.str.startswith("STM32")]
# Change some GPIO mapping for Nucleo-H743ZI2
# Created a mapping from old pins to new pins
old_to_new_dict = {
# Old : New
"PC1" :"PB1" ,
"PC4" :"PC2" ,
"PC5" :"PF10" ,
"PA1" :"PB2" ,
"PA7" :"PE9" ,
"PA8" :"PF2" ,
"PA9" :"PF1" ,
"PA10" :"PF0" ,
"PB1" :"PF4" ,
"PC2" :"PF5" ,
"PA2" :"PF6" ,
"PB6" :"PG6" ,
"PE13" :"PG12" ,
"PF14" :"PE14" ,
"PE14" :"PE6" ,
}
# Extract data from stm32 dataframe to a dictionary
gpio_dict = {}
global_gpio_dict = {}
for index, row in stm32.iterrows():
# Replace old pins with new pins
# Note: There are 2 PE6 pins on Nucleo-H743ZI2
# This will remove 1 mapping of PE6, keys cannot be duplicated
key = row["Net"].split("_")[1]
if key in old_to_new_dict:
key = old_to_new_dict.pop(key)
dict_entry = {key: row["Designator"]}
# Insert mappings into dictionary
if "BIN" in row["Pin Name"]:
global_gpio_dict.update(dict_entry)
else:
gpio_dict.update(dict_entry)
return gpio_dict, global_gpio_dict
# Function to provide mapping EEM pins to differential pins
# Usage:
# Positive pin: <returned>[<eem_port_val>][<eem_port_ldvs_num>][0]
# Negative pin: <returned>[<eem_port_val>][<eem_port_ldvs_num>][1]
def diffMapping():
eem0 = [
# P+ve , N-ve
[ "J3" , "H1" ],
[ "F5" , "B1" ],
[ "C1" , "C2" ],
[ "F4" , "D2" ],
[ "G5" , "D1" ],
[ "G4" , "E3" ],
[ "H5" , "E2" ],
[ "G3" , "F3" ],
]
eem1 = [
# P+ve , N-ve
[ "L6" , "L3" ],
[ "H6" , "F1" ],
[ "H4" , "G2" ],
[ "J4" , "H2" ],
[ "J2" , "J1" ],
[ "K1" , "K3" ],
[ "L4" , "L1" ],
[ "K4" , "M1" ],
]
eem2 = [
# P+ve , N-ve
[ "J5" , "G1" ],
[ "K5" , "M2" ],
[ "L7" , "N2" ],
[ "M6" , "M3" ],
[ "L5" , "N3" ],
[ "P1" , "M4" ],
[ "P2" , "M5" ],
[ "R1" , "N4" ],
]
return [eem0, eem1, eem2]

90
nmigen/resources.py Normal file
View File

@ -0,0 +1,90 @@
from nmigen.build import *
__all__ = ["I2CResource", "GPIOResources", "DiffResources"]
def I2CResource(*args, sda, scl, conn=None, attrs=None, role="host"):
assert role in ("host", "device")
io = []
# sda line: I/O port for the data line
io.append(Subsignal("sda", Pins(sda, dir="io", conn=conn, assert_width=1)))
# sck line: I2C clock signal outputs from master to slave
if role == "host":
io.append(Subsignal("scl", Pins(scl, dir="o", conn=conn, assert_width=1)))
else: #device
io.append(Subsignal("scl", Pins(scl, dir="i", conn=conn, assert_width=1)))
if attrs is not None:
io.append(attrs)
return Resource.family(*args, default_name="i2c", ios=io)
# Auto create a resource list given a set of iCE40 pins and STM32 pin names (pins_dict)
def GPIOResources(*args, pins_dict, dir = "o", invert=False, conn=None, attrs=None):
# Check data integrity: pins_dict must be a dict AND port must be from a to k
assert isinstance(pins_dict, dict)
# Debug: dir == "o"
assert dir == "o"
# List of resources to be returned
resources = []
for STM32_pin, iCE40_pin in pins_dict.items():
# Set all gpio pins to be output only for the time being
# TODO: Allow dir argument.
ios = [Pins(iCE40_pin, dir=dir, invert=invert, conn=conn)]
if attrs is not None:
ios.append(attrs)
# Extract GPIO port and port number from STM32_pin
# Strip "P" from P<port><port_num>
if STM32_pin.startswith('P'):
STM32_pin = STM32_pin[1:]
# Acquire port from <port><port_num>
port = STM32_pin[0].lower()
port_num = int(STM32_pin[1:])
# Insert gpio<port>.<portNum> into resources list
resources.append(Resource.family(*args, port_num, default_name=("gpio"+port), ios=ios))
return resources
# Auto create a resource list for differential I/O
def DiffResources(*args, eem_pins, invert=False, conn=None, attrs=None, dir):
# TODO: Everything
# assert dimensionality
assert isinstance(eem_pins, list)
assert isinstance(eem_pins[0], list)
assert isinstance(eem_pins[0][0], list)
assert isinstance(eem_pins[0][0][0], str)
# assert direction to be either input or output
# reject tristate or bidirectional pin
assert dir in ("i", "o")
if __name__ == "__main__":
from pin_mapper import *
eem = diffMapping()
DiffResources(eem_pins = eem, dir = "o",
attrs=Attrs(IO_STANDARD="SB_LVCMOS")
)