forked from M-Labs/humpback-dds
nmigen: migrated
This commit is contained in:
parent
7351a9d58a
commit
8f1237a0a6
BIN
nmigen/FPGA_pins.xlsx
Normal file
BIN
nmigen/FPGA_pins.xlsx
Normal file
Binary file not shown.
24
nmigen/fpga_config.py
Normal file
24
nmigen/fpga_config.py
Normal 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
112
nmigen/humpback.py
Normal 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
105
nmigen/pin_mapper.py
Normal 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
90
nmigen/resources.py
Normal 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")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user