Skip to content

Commit

Permalink
Merge pull request #230 from yrabbit/make-bits
Browse files Browse the repository at this point in the history
Replace numpy arrays with lists.
  • Loading branch information
yrabbit authored Feb 9, 2024
2 parents 4ad5c14 + c920214 commit f43a0b7
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 47 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 f43a0b7

Please sign in to comment.