-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from descampsa/lattice_machxo2
Added reader for Lattice FPGAs (except the iCE40).
- Loading branch information
Showing
2 changed files
with
138 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
# MIT license | ||
# | ||
# Copyright (C) 2015 by XESS Corp. | ||
# | ||
# Permission is hereby granted, free of charge, to any person obtaining a copy | ||
# of this software and associated documentation files (the "Software"), to deal | ||
# in the Software without restriction, including without limitation the rights | ||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
# copies of the Software, and to permit persons to whom the Software is | ||
# furnished to do so, subject to the following conditions: | ||
# | ||
# The above copyright notice and this permission notice shall be included in | ||
# all copies or substantial portions of the Software. | ||
# | ||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
# THE SOFTWARE. | ||
|
||
from __future__ import absolute_import | ||
import csv | ||
import copy | ||
import os.path | ||
from collections import defaultdict | ||
from .common import * | ||
from .kipart import * | ||
|
||
|
||
def lattice_reader(csv_file): | ||
'''Extract the pin data from a Lattice CSV file and return a dictionary of pin data. | ||
Both csv files available on Lattice website and csv files exported from Diamond are supported. | ||
ICE40 family is NOT supported, since they use a completely different format.''' | ||
|
||
# Create a dictionary that uses the package name as key. Each entry in this dictionary | ||
# uses the unit numbers as keys. Each entry in this dictionary contains another | ||
# dictionary that uses the side of the symbol as a key. Each entry in that dictionary | ||
# uses the pin names in that unit and on that side as keys. Each entry in that | ||
# dictionary is a list of Pin objects with each Pin object having the same name | ||
# as the dictionary key. So the pins are separated into units at the top level, and then | ||
# the sides of the symbol, and then the pins with the same name that are on that side | ||
# of the unit. | ||
pin_data = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(list)))) | ||
|
||
# Parse the first lines, until we find the column title row | ||
global_device_name='' | ||
global_package_name='' | ||
while True: | ||
pos=csv_file.tell() | ||
line = csv_file.readline() | ||
if line[0:8]=='#DEVICE=': | ||
global_device_name=line[9:].split(',')[0].strip().upper().replace('-', '_').replace(' ','_') | ||
if line[0:9]=='#PACKAGE=': | ||
global_package_name=line[10:].split(',')[0].strip().upper().replace('-', '_').replace(' ','_') | ||
test_field = line.split(',')[0].strip().upper() | ||
if test_field == 'INDEX' or test_field == 'PAD': | ||
break | ||
csv_file.seek(pos) | ||
|
||
# If no device name found in comments, get part number from file name | ||
if global_device_name: | ||
part_num = global_device_name | ||
else: | ||
part_num = os.path.splitext(os.path.basename(csv_file.name))[0] | ||
part_num = part_num.upper().split('PINOUT')[0].replace('-', '_').replace(' ','_') | ||
|
||
# Create a csv dict reader, from the column title row | ||
csv_reader = csv.DictReader(csv_file, skipinitialspace=True) | ||
|
||
# Some field name normalization : there is some variation in capitalization, | ||
# 'Pin/Ball function' is sometimes 'Pin/Ball', and there may be references to comments after the field name (eg 'type(1)') | ||
csv_reader.fieldnames = [f.split('(')[0] for f in csv_reader.fieldnames] | ||
csv_reader.fieldnames = [f.strip().upper() for f in csv_reader.fieldnames] | ||
csv_reader.fieldnames = ['PIN/BALL FUNCTION' if f == 'PIN/BALL' else f for f in csv_reader.fieldnames] | ||
|
||
# If we have found a package name in comments, and a 'Pin Number' field is available, | ||
# we use this package name, and replace the 'Pin Number' name by its name | ||
# else we consider this is a multi-package csv file, and any column with an unknown field name is considered a package name | ||
# In the multi-package case, package names are normalized, and DQS variants are ignored | ||
if global_package_name and 'PIN NUMBER' in csv_reader.fieldnames: | ||
csv_reader.fieldnames = [global_package_name if f=='PIN NUMBER' else f for f in csv_reader.fieldnames] | ||
package = [global_package_name] | ||
else: | ||
known_fields = ['INDEX', 'TYPE', 'PAD', 'PIN/BALL FUNCTION', 'BANK', 'DUAL FUNCTION', 'DIFFERENTIAL', 'HIGH SPEED', 'DQS', 'I/O GROUPING', 'PIN NUMBER'] | ||
csv_reader.fieldnames = [x if x in known_fields else x.upper().replace('-', '_').replace(' ','_') for x in csv_reader.fieldnames] | ||
package = [x for x in csv_reader.fieldnames if x and x not in known_fields and not x.endswith('_DQS')] | ||
|
||
if not package: | ||
print 'Warning : no package found, exiting' | ||
return | ||
|
||
#Process the pins line-by-line | ||
for index,row in enumerate(csv_reader): | ||
pin = copy.copy(DEFAULT_PIN) | ||
pin.index = index | ||
pin.name = row['PIN/BALL FUNCTION'] | ||
if not row['BANK'] or row['BANK'] == '-' or row['BANK'] == ' ': | ||
pin.unit = 1 | ||
else: | ||
pin.unit = int(row['BANK'])+2 | ||
|
||
# The type of the pin isn't given in the text file, so we'll have to infer it | ||
# from the name of the pin. Pin names starting with the following prefixes | ||
# are assigned the given pin type. | ||
DEFAULT_PIN_TYPE = 'io' # Assign this pin type if name inference can't be made. | ||
PIN_TYPE_PREFIXES = [ | ||
(r'VCC', 'power_in'), | ||
(r'GND', 'power_in'), | ||
(r'NC', 'no_connect'), | ||
] | ||
for prefix, typ in PIN_TYPE_PREFIXES: | ||
if re.match(prefix, pin.name, re.IGNORECASE): | ||
pin.type = typ | ||
break | ||
else: | ||
pin.type = DEFAULT_PIN_TYPE | ||
|
||
# Same for pin side, in order to have VCC at the top and GND at the bottom | ||
PIN_SIDE_PREFIXES = [ | ||
(r'VCC', 'top'), | ||
(r'GND', 'bottom'), | ||
] | ||
for prefix, s in PIN_SIDE_PREFIXES: | ||
if re.match(prefix, pin.name, re.IGNORECASE): | ||
pin.side = s | ||
|
||
for p in package: | ||
if row[p] and row[p] != '-' and row[p] != ' ': | ||
pin.num = row[p] | ||
pin_data[p][pin.unit][pin.side][pin.name].append(copy.copy(pin)) | ||
|
||
|
||
for p in package: | ||
yield part_num+'_'+p, 'U', pin_data[p] # Return the dictionary of pins for the package p | ||
|