Skip to content

Commit

Permalink
Replace numpy arrays with lists.
Browse files Browse the repository at this point in the history
Dumb replacement of functions. The functions work only to the extent
required, no more.

Creating chip databases (this includes fuzzing etc), packaging and
unpacking work.

Special moments:
   * you will still need numpy to create the PNG, but the import only
     happens when that function is called.
   * compression packaging needs to be tested.
   * perhaps somewhere in places that I rarely use there are numpy
     calls.

Signed-off-by: YRabbit <[email protected]>
  • Loading branch information
yrabbit committed Jan 25, 2024
1 parent fb9a8df commit 31dbe99
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 46 deletions.
113 changes: 113 additions & 0 deletions apycula/bitmatrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@

def fliplr(bmp):
"""
Flips the entries in each row in the left/right direction.
Returns a new matrix.
"""
return [row[::-1] for row in bmp]

def flipud(bmp):
"""
Reverse the order of elements in each column (up/down).
Returns a refence.
"""
return bmp[::-1]

def vstack(bmp_0, bmp_1):
"""
Stack matrices in sequence vertically (row wise).
Returns a reference.
"""
return [*bmp_0, *bmp_1]

def hstack(bmp_0, bmp_1):
"""
Stack matrices in sequence horizontally (column wise).
Returns a new matrix.
"""
return [bmp[0] + bmp[1] for bmp in zip(bmp_0, bmp_1)]

def shape(bmp):
"""
Return the shape of a matrix.
"""
return [len(bmp), len(bmp[0])]

def ones(rows, cols):
"""
Returns a new matrix of given shape, filled with ones.
"""
return [[1] * cols for i in range(rows)]

def zeros(rows, cols):
"""
Returns a new matrix of given shape, filled with zeros.
"""
return [[0] * cols for i in range(rows)]

def packbits(bmp, axis = None):
"""
Packs the elements of a bitmap into bytes.
[1, 1, 0, 0, 0] -> [24] # [5'b11000]
Returns a list of bytes.
"""
byte_list = []
if not axis:
for bmp_r in bmp:
for col in range(shape(bmp)[1] // 8):
bcol = col << 3
byte_list.append((bmp_r[bcol] << 7) + (bmp_r[bcol + 1] << 6) + (bmp_r[bcol + 2] << 5) +
(bmp_r[bcol + 3] << 4) + (bmp_r[bcol + 4] << 3) + (bmp_r[bcol + 5] << 2) +
(bmp_r[bcol + 6] << 1) + bmp_r[bcol + 7])
else:
for bmp_r in bmp:
byte_list.append([])
byte_list_r = byte_list[-1]
for col in range(shape(bmp)[1] // 8):
bcol = col << 3
byte_list_r.append((bmp_r[bcol] << 7) + (bmp_r[bcol + 1] << 6) + (bmp_r[bcol + 2] << 5) +
(bmp_r[bcol + 3] << 4) + (bmp_r[bcol + 4] << 3) + (bmp_r[bcol + 5] << 2) +
(bmp_r[bcol + 6] << 1) + bmp_r[bcol + 7])
return byte_list

def xor(bmp_0, bmp_1):
"""
Bitwise XOR
Returns a new matrix
"""
return [[ vals[0] ^ vals[1]for vals in zip(row[0], row[1])] for row in zip(bmp_0, bmp_1)]

def histogram(lst, bins):
"""
Compute the histogram of a list.
Returns a list of counters.
"""
l_bins = len(bins) - 1
r_lst = [0] * l_bins
for val in lst:
for i in range(l_bins):
if val in range(bins[i], bins[i + 1]) or (i == l_bins - 1 and val == bins[-1]):
r_lst[i] += 1
return r_lst

def any(bmp):
"""
Test whether any matrix element evaluates to True.
"""
for row in bmp:
for val in row:
if val:
return True
return False

def nonzero(bmp):
"""
Return the indices of the elements that are non-zero.
"""
res = ([], [])
for ri, row in enumerate(bmp):
for ci, val in enumerate(row):
if val:
res[0].append(ri)
res[1].append(ci)
return res
26 changes: 13 additions & 13 deletions apycula/bslib.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from math import ceil
import numpy as np
import crc
from apycula import bitmatrix

crc16arc = crc.Configuration(width=16, polynomial=0x8005, reverse_input=True, reverse_output=True)

Expand Down Expand Up @@ -71,7 +71,7 @@ def read_bitstream(fname):
bitmap.append(bitarr(line, padding))
frames = max(0, frames-1)

return np.fliplr(np.array(bitmap)), hdr, ftr
return bitmatrix.fliplr(bitmap), hdr, ftr

def compressLine(line, key8Z, key4Z, key2Z):
newline = []
Expand All @@ -82,26 +82,26 @@ def compressLine(line, key8Z, key4Z, key2Z):
return newline

def write_bitstream_with_bsram_init(fname, bs, hdr, ftr, compress, bsram_init):
new_bs = np.vstack((bs, bsram_init))
new_bs = bitmatrix.vstack(bs, bsram_init)
new_hdr = hdr.copy()
frames = int.from_bytes(new_hdr[-1][2:], 'big') + bsram_init.shape[0]
frames = int.from_bytes(new_hdr[-1][2:], 'big') + bitmatrix.shape(bsram_init)[0]
new_hdr[-1][2:] = frames.to_bytes(2, 'big')
write_bitstream(fname, new_bs, new_hdr, ftr, compress)

def write_bitstream(fname, bs, hdr, ftr, compress):
bs = np.fliplr(bs)
bs = bitmatrix.fliplr(bs)
if compress:
padlen = (ceil(bs.shape[1] / 64) * 64) - bs.shape[1]
padlen = (ceil(bitmatrix.shape(bs)[1] / 64) * 64) - bitmatrix.shape(bs)[1]
else:
padlen = bs.shape[1] % 8
pad = np.ones((bs.shape[0], padlen), dtype=np.uint8)
bs = np.hstack([pad, bs])
assert bs.shape[1] % 8 == 0
bs=np.packbits(bs, axis=1)
padlen = bitmatrix.shape(bs)[1] % 8
pad = bitmatrix.ones(bitmatrix.shape(bs)[0], padlen)
bs = bitmatrix.hstack(pad, bs)
assert bitmatrix.shape(bs)[1] % 8 == 0
bs=bitmatrix.packbits(bs, axis = 1)

if compress:
# search for smallest values not used in the bitstream
lst, _ = np.histogram(bs, bins=[i for i in range(256)])
lst = bitmatrix.histogram(bs, bins=[i for i in range(257)]) # 257 iso that the last basket is [255, 256] and not [254, 255]
[key8Z, key4Z, key2Z] = [i for i,val in enumerate(lst) if val==0][0:3]

# update line 0x51 with keys
Expand Down Expand Up @@ -140,7 +140,7 @@ def display(fname, data):
im = Image.frombytes(
mode='1',
size=data.shape[::-1],
data=np.packbits(data, axis=1))
data=bitmatrix.packbits(data, axis = 1))
if fname:
im.save(fname)
return im
22 changes: 14 additions & 8 deletions apycula/chipdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import copy
from functools import reduce
from collections import namedtuple
import numpy as np
from apycula.dat19 import Datfile
import apycula.fuse_h4x as fuse
from apycula.wirenames import wirenames, wirenumbers, clknames, clknumbers, hclknames, hclknumbers
from apycula import pindef
from apycula import bitmatrix

# the character that marks the I/O attributes that come from the nextpnr
mode_attr_sep = '&'
Expand Down Expand Up @@ -75,7 +75,7 @@ class Device:
pin_bank: Dict[str, int] = field(default_factory = dict)
cmd_hdr: List[ByteString] = field(default_factory=list)
cmd_ftr: List[ByteString] = field(default_factory=list)
template: np.ndarray = None
template: List[List[int]] = None
# allowable values of bel attributes
# {table_name: [(attr_id, attr_value)]}
logicinfo: Dict[str, List[Tuple[int, int]]] = field(default_factory=dict)
Expand Down Expand Up @@ -2093,25 +2093,31 @@ def tile_bitmap(dev, bitmap, empty=False):
for jdx, td in enumerate(row):
w = td.width
h = td.height
tile = bitmap[y:y+h,x:x+w]
if tile.any() or empty:
tile = [row[x:x+w] for row in bitmap[y:y+h]]
if bitmatrix.any(tile) or empty:
res[(idx, jdx)] = tile
x+=w
y+=h

return res

def fuse_bitmap(db, bitmap):
res = np.zeros((db.height, db.width), dtype=np.uint8)
res = bitmatrix.zeros(db.height, db.width)
y = 0
for idx, row in enumerate(db.grid):
x=0
for jdx, td in enumerate(row):
w = td.width
h = td.height
res[y:y+h,x:x+w] = bitmap[(idx, jdx)]
x+=w
y+=h
y0 = y
for row in bitmap[(idx, jdx)]:
x0 = x
for val in row:
res[y0][x0] = val
x0 += 1
y0 += 1
x += w
y += h

return res

Expand Down
5 changes: 3 additions & 2 deletions apycula/clock_fuzzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from apycula import fuse_h4x
from apycula import dat19
from apycula import gowin_unpack
from apycula import bitmatrix
from apycula.wirenames import clknumbers

def dff(mod, cst, row, col, clk=None):
Expand Down Expand Up @@ -99,7 +100,7 @@ def quadrants():

res = {}
for (row, col), (mybs, *_) in zip(idxes, pnr_res):
sweep_tiles = fuse_h4x.tile_bitmap(fse, mybs^pnr.bitmap)
sweep_tiles = fuse_h4x.tile_bitmap(fse, bitmatrix.xor(mybs, pnr.bitmap))

# find which tap was used
taps = [r for (r, c, typ), t in sweep_tiles.items() if typ in {13, 14, 15, 16, 18, 19}]
Expand Down Expand Up @@ -148,7 +149,7 @@ def center_muxes(ct, rows, cols):
base = pnr.bitmap
for i, (bs_sweep, *_) in enumerate(pnr_res):
pin = true_pins[i]
new = base ^ bs_sweep
new = bitmatrix.xor(base, bs_sweep)
tiles = chipdb.tile_bitmap(db, new)

try:
Expand Down
36 changes: 25 additions & 11 deletions apycula/fuse_h4x.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys
import numpy as np
import random
from apycula import bitmatrix

def rint(f, w):
val = int.from_bytes(f.read(w), 'little', signed=True)
Expand Down Expand Up @@ -74,7 +74,7 @@ def readOneFile(f, fuselength):
def render_tile(d, ttyp):
w = d[ttyp]['width']
h = d[ttyp]['height']
tile = np.zeros((h, w), np.uint8)#+(255-ttyp)
tile = bitmatrix.zeros(h, w)#+(255-ttyp)
for start, table in [(2, 'shortval'), (2, 'wire'), (16, 'longval'),
(1, 'longfuse'), (0, 'const')]:
if table in d[ttyp]:
Expand Down Expand Up @@ -104,24 +104,33 @@ def render_bitmap(d):
tiles = d['header']['grid'][61]
width = sum([d[i]['width'] for i in tiles[0]])
height = sum([d[i[0]]['height'] for i in tiles])
bitmap = np.zeros((height, width), np.uint8)
bitmap = bitmatrix.zeros(height, width)
y = 0
for row in tiles:
x=0
for typ in row:
#if typ==12: pdb.set_trace()
td = d[typ]
w = td['width']
h = td['height']
#bitmap[y:y+h,x:x+w] += render_tile(d, typ)
bitmap[y:y+h,x:x+w] = typ
#bitmap[y:y+h,x:x+w] = typ
rtile = render_tile(d, typ)
y0 = y
for row in rtile:
x0 = x
for val in row:
bitmap[y0][x0] += val
x0 += 1
y0 += 1
x+=w
y+=h

return bitmap

def display(fname, data):
from PIL import Image
import numpy as np
data = np.array(data, dtype = np.uint8)
im = Image.frombytes(
mode='P',
size=data.shape[::-1],
Expand All @@ -148,12 +157,11 @@ def tile_bitmap(d, bitmap, empty=False):
for idx, row in enumerate(tiles):
x=0
for jdx, typ in enumerate(row):
#if typ==87: pdb.set_trace()
td = d[typ]
w = td['width']
h = td['height']
tile = bitmap[y:y+h,x:x+w]
if tile.any() or empty:
tile = [row[x:x+w] for row in bitmap[y:y+h]]
if bitmatrix.any(tile) or empty:
res[(idx, jdx, typ)] = tile
x+=w
y+=h
Expand All @@ -164,15 +172,21 @@ def fuse_bitmap(d, bitmap):
tiles = d['header']['grid'][61]
width = sum([d[i]['width'] for i in tiles[0]])
height = sum([d[i[0]]['height'] for i in tiles])
res = np.zeros((height, width), dtype=np.uint8)
res = bitmatrix.zeros(height, width)
y = 0
for idx, row in enumerate(tiles):
x=0
for jdx, typ in enumerate(row):
td = d[typ]
w = td['width']
h = td['height']
res[y:y+h,x:x+w] = bitmap[(idx, jdx, typ)]
y0 = y
for row in bitmap[(idx, jdx, typ)]:
x0 = x
for val in row:
res[y0][x0] = val
x0 += 1
y0 += 1
x+=w
y+=h

Expand Down Expand Up @@ -207,7 +221,7 @@ def scan_fuses(d, ttyp, tile):
w = d[ttyp]['width']
h = d[ttyp]['height']
fuses = []
rows, cols = np.where(tile==1)
rows, cols = bitmatrix.nonzero(tile)
for row, col in zip(rows, cols):
# ripe for optimization
for fnum, fuse in enumerate(d['header']['fuse'][1]):
Expand Down
Loading

0 comments on commit 31dbe99

Please sign in to comment.