Skip to content

Commit f43a0b7

Browse files
authored
Merge pull request #230 from yrabbit/make-bits
Replace numpy arrays with lists.
2 parents 4ad5c14 + c920214 commit f43a0b7

File tree

9 files changed

+181
-47
lines changed

9 files changed

+181
-47
lines changed

apycula/bitmatrix.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
2+
def fliplr(bmp):
3+
"""
4+
Flips the entries in each row in the left/right direction.
5+
Returns a new matrix.
6+
"""
7+
return [row[::-1] for row in bmp]
8+
9+
def flipud(bmp):
10+
"""
11+
Reverse the order of elements in each column (up/down).
12+
Returns a refence.
13+
"""
14+
return bmp[::-1]
15+
16+
def vstack(bmp_0, bmp_1):
17+
"""
18+
Stack matrices in sequence vertically (row wise).
19+
Returns a reference.
20+
"""
21+
return [*bmp_0, *bmp_1]
22+
23+
def hstack(bmp_0, bmp_1):
24+
"""
25+
Stack matrices in sequence horizontally (column wise).
26+
Returns a new matrix.
27+
"""
28+
return [bmp[0] + bmp[1] for bmp in zip(bmp_0, bmp_1)]
29+
30+
def shape(bmp):
31+
"""
32+
Return the shape of a matrix.
33+
"""
34+
return [len(bmp), len(bmp[0])]
35+
36+
def ones(rows, cols):
37+
"""
38+
Returns a new matrix of given shape, filled with ones.
39+
"""
40+
return [[1] * cols for i in range(rows)]
41+
42+
def zeros(rows, cols):
43+
"""
44+
Returns a new matrix of given shape, filled with zeros.
45+
"""
46+
return [[0] * cols for i in range(rows)]
47+
48+
def packbits(bmp, axis = None):
49+
"""
50+
Packs the elements of a bitmap into bytes.
51+
[1, 1, 0, 0, 0] -> [24] # [5'b11000]
52+
Returns a list of bytes.
53+
"""
54+
byte_list = []
55+
if not axis:
56+
for bmp_r in bmp:
57+
for col in range(shape(bmp)[1] // 8):
58+
bcol = col << 3
59+
byte_list.append((bmp_r[bcol] << 7) + (bmp_r[bcol + 1] << 6) + (bmp_r[bcol + 2] << 5) +
60+
(bmp_r[bcol + 3] << 4) + (bmp_r[bcol + 4] << 3) + (bmp_r[bcol + 5] << 2) +
61+
(bmp_r[bcol + 6] << 1) + bmp_r[bcol + 7])
62+
else:
63+
for bmp_r in bmp:
64+
byte_list.append([])
65+
byte_list_r = byte_list[-1]
66+
for col in range(shape(bmp)[1] // 8):
67+
bcol = col << 3
68+
byte_list_r.append((bmp_r[bcol] << 7) + (bmp_r[bcol + 1] << 6) + (bmp_r[bcol + 2] << 5) +
69+
(bmp_r[bcol + 3] << 4) + (bmp_r[bcol + 4] << 3) + (bmp_r[bcol + 5] << 2) +
70+
(bmp_r[bcol + 6] << 1) + bmp_r[bcol + 7])
71+
return byte_list
72+
73+
def xor(bmp_0, bmp_1):
74+
"""
75+
Bitwise XOR
76+
Returns a new matrix
77+
"""
78+
return [[ vals[0] ^ vals[1]for vals in zip(row[0], row[1])] for row in zip(bmp_0, bmp_1)]
79+
80+
def histogram(lst, bins):
81+
"""
82+
Compute the histogram of a list.
83+
Returns a list of counters.
84+
"""
85+
l_bins = len(bins) - 1
86+
r_lst = [0] * l_bins
87+
for val in lst:
88+
for i in range(l_bins):
89+
if val in range(bins[i], bins[i + 1]) or (i == l_bins - 1 and val == bins[-1]):
90+
r_lst[i] += 1
91+
return r_lst
92+
93+
def any(bmp):
94+
"""
95+
Test whether any matrix element evaluates to True.
96+
"""
97+
for row in bmp:
98+
for val in row:
99+
if val:
100+
return True
101+
return False
102+
103+
def nonzero(bmp):
104+
"""
105+
Return the indices of the elements that are non-zero.
106+
"""
107+
res = ([], [])
108+
for ri, row in enumerate(bmp):
109+
for ci, val in enumerate(row):
110+
if val:
111+
res[0].append(ri)
112+
res[1].append(ci)
113+
return res

apycula/bslib.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from math import ceil
2-
import numpy as np
32
import crc
3+
from apycula import bitmatrix
44

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

@@ -71,7 +71,7 @@ def read_bitstream(fname):
7171
bitmap.append(bitarr(line, padding))
7272
frames = max(0, frames-1)
7373

74-
return np.fliplr(np.array(bitmap)), hdr, ftr
74+
return bitmatrix.fliplr(bitmap), hdr, ftr
7575

7676
def compressLine(line, key8Z, key4Z, key2Z):
7777
newline = []
@@ -82,26 +82,26 @@ def compressLine(line, key8Z, key4Z, key2Z):
8282
return newline
8383

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

9191
def write_bitstream(fname, bs, hdr, ftr, compress):
92-
bs = np.fliplr(bs)
92+
bs = bitmatrix.fliplr(bs)
9393
if compress:
94-
padlen = (ceil(bs.shape[1] / 64) * 64) - bs.shape[1]
94+
padlen = (ceil(bitmatrix.shape(bs)[1] / 64) * 64) - bitmatrix.shape(bs)[1]
9595
else:
96-
padlen = bs.shape[1] % 8
97-
pad = np.ones((bs.shape[0], padlen), dtype=np.uint8)
98-
bs = np.hstack([pad, bs])
99-
assert bs.shape[1] % 8 == 0
100-
bs=np.packbits(bs, axis=1)
96+
padlen = bitmatrix.shape(bs)[1] % 8
97+
pad = bitmatrix.ones(bitmatrix.shape(bs)[0], padlen)
98+
bs = bitmatrix.hstack(pad, bs)
99+
assert bitmatrix.shape(bs)[1] % 8 == 0
100+
bs=bitmatrix.packbits(bs, axis = 1)
101101

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

107107
# update line 0x51 with keys
@@ -140,7 +140,7 @@ def display(fname, data):
140140
im = Image.frombytes(
141141
mode='1',
142142
size=data.shape[::-1],
143-
data=np.packbits(data, axis=1))
143+
data=bitmatrix.packbits(data, axis = 1))
144144
if fname:
145145
im.save(fname)
146146
return im

apycula/chipdb.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
import copy
66
from functools import reduce
77
from collections import namedtuple
8-
import numpy as np
98
from apycula.dat19 import Datfile
109
import apycula.fuse_h4x as fuse
1110
from apycula.wirenames import wirenames, wirenumbers, clknames, clknumbers, hclknames, hclknumbers
1211
from apycula import pindef
12+
from apycula import bitmatrix
1313

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

21022102
return res
21032103

21042104
def fuse_bitmap(db, bitmap):
2105-
res = np.zeros((db.height, db.width), dtype=np.uint8)
2105+
res = bitmatrix.zeros(db.height, db.width)
21062106
y = 0
21072107
for idx, row in enumerate(db.grid):
21082108
x=0
21092109
for jdx, td in enumerate(row):
21102110
w = td.width
21112111
h = td.height
2112-
res[y:y+h,x:x+w] = bitmap[(idx, jdx)]
2113-
x+=w
2114-
y+=h
2112+
y0 = y
2113+
for row in bitmap[(idx, jdx)]:
2114+
x0 = x
2115+
for val in row:
2116+
res[y0][x0] = val
2117+
x0 += 1
2118+
y0 += 1
2119+
x += w
2120+
y += h
21152121

21162122
return res
21172123

apycula/clock_fuzzer.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from apycula import fuse_h4x
1010
from apycula import dat19
1111
from apycula import gowin_unpack
12+
from apycula import bitmatrix
1213
from apycula.wirenames import clknumbers
1314

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

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

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

154155
try:

apycula/fuse_h4x.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sys
2-
import numpy as np
32
import random
3+
from apycula import bitmatrix
44

55
def rint(f, w):
66
val = int.from_bytes(f.read(w), 'little', signed=True)
@@ -74,7 +74,7 @@ def readOneFile(f, fuselength):
7474
def render_tile(d, ttyp):
7575
w = d[ttyp]['width']
7676
h = d[ttyp]['height']
77-
tile = np.zeros((h, w), np.uint8)#+(255-ttyp)
77+
tile = bitmatrix.zeros(h, w)#+(255-ttyp)
7878
for start, table in [(2, 'shortval'), (2, 'wire'), (16, 'longval'),
7979
(1, 'longfuse'), (0, 'const')]:
8080
if table in d[ttyp]:
@@ -104,24 +104,33 @@ def render_bitmap(d):
104104
tiles = d['header']['grid'][61]
105105
width = sum([d[i]['width'] for i in tiles[0]])
106106
height = sum([d[i[0]]['height'] for i in tiles])
107-
bitmap = np.zeros((height, width), np.uint8)
107+
bitmap = bitmatrix.zeros(height, width)
108108
y = 0
109109
for row in tiles:
110110
x=0
111111
for typ in row:
112-
#if typ==12: pdb.set_trace()
113112
td = d[typ]
114113
w = td['width']
115114
h = td['height']
116115
#bitmap[y:y+h,x:x+w] += render_tile(d, typ)
117-
bitmap[y:y+h,x:x+w] = typ
116+
#bitmap[y:y+h,x:x+w] = typ
117+
rtile = render_tile(d, typ)
118+
y0 = y
119+
for row in rtile:
120+
x0 = x
121+
for val in row:
122+
bitmap[y0][x0] += val
123+
x0 += 1
124+
y0 += 1
118125
x+=w
119126
y+=h
120127

121128
return bitmap
122129

123130
def display(fname, data):
124131
from PIL import Image
132+
import numpy as np
133+
data = np.array(data, dtype = np.uint8)
125134
im = Image.frombytes(
126135
mode='P',
127136
size=data.shape[::-1],
@@ -148,12 +157,11 @@ def tile_bitmap(d, bitmap, empty=False):
148157
for idx, row in enumerate(tiles):
149158
x=0
150159
for jdx, typ in enumerate(row):
151-
#if typ==87: pdb.set_trace()
152160
td = d[typ]
153161
w = td['width']
154162
h = td['height']
155-
tile = bitmap[y:y+h,x:x+w]
156-
if tile.any() or empty:
163+
tile = [row[x:x+w] for row in bitmap[y:y+h]]
164+
if bitmatrix.any(tile) or empty:
157165
res[(idx, jdx, typ)] = tile
158166
x+=w
159167
y+=h
@@ -164,15 +172,21 @@ def fuse_bitmap(d, bitmap):
164172
tiles = d['header']['grid'][61]
165173
width = sum([d[i]['width'] for i in tiles[0]])
166174
height = sum([d[i[0]]['height'] for i in tiles])
167-
res = np.zeros((height, width), dtype=np.uint8)
175+
res = bitmatrix.zeros(height, width)
168176
y = 0
169177
for idx, row in enumerate(tiles):
170178
x=0
171179
for jdx, typ in enumerate(row):
172180
td = d[typ]
173181
w = td['width']
174182
h = td['height']
175-
res[y:y+h,x:x+w] = bitmap[(idx, jdx, typ)]
183+
y0 = y
184+
for row in bitmap[(idx, jdx, typ)]:
185+
x0 = x
186+
for val in row:
187+
res[y0][x0] = val
188+
x0 += 1
189+
y0 += 1
176190
x+=w
177191
y+=h
178192

@@ -207,7 +221,7 @@ def scan_fuses(d, ttyp, tile):
207221
w = d[ttyp]['width']
208222
h = d[ttyp]['height']
209223
fuses = []
210-
rows, cols = np.where(tile==1)
224+
rows, cols = bitmatrix.nonzero(tile)
211225
for row, col in zip(rows, cols):
212226
# ripe for optimization
213227
for fnum, fuse in enumerate(d['header']['fuse'][1]):

0 commit comments

Comments
 (0)