bitfile.py
1 # Copyright 2007 Jeff Epler <jepler@unpythonic.net> 2 # 3 # This program is free software; you can redistribute it and/or modify 4 # it under the terms of the GNU General Public License as published by 5 # the Free Software Foundation; either version 2 of the License, or 6 # (at your option) any later version. 7 # 8 # This program is distributed in the hope that it will be useful, 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 # GNU General Public License for more details. 12 # 13 # You should have received a copy of the GNU General Public License 14 # along with this program; if not, write to the Free Software 15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16 # 17 # 18 # Info about Xilinx bitfiles: 19 # 20 # The format consists of several variable length chunks, preceded by what 21 # seems to be a constant header (a magic number). 22 # 23 # After the header, each "chunk" consists of a one byte "tag", a two or 24 # four byte length, and "length" bytes of data (the body). 25 # 26 # In some chunks, the body is a zero terminated printable string. In 27 # others it is a blob of binary data. The file format doesn't care. 28 # 29 # Standard Xilinx files use 5 chunks: 'a' through 'd' are zero-terminated 30 # strings with information about the file. 'e' is a large binary blob 31 # with the actual bitstream in it. Xilinx uses 2 byte lengths for chunks 32 # 'a' thru 'd', and 4 bytes for chunk 'e'. This library allows other 33 # chunks, and assume that all have 4 byte lengths, except 'a' thru 'd'. 34 # 35 import struct 36 37 class BitFile: 38 MAXCHUNKS = 50 39 SMALLCHUNKS = "abcd" 40 MAGIC = "\x00\x09" \ 41 "\x0f\xf0\x0f\xf0" \ 42 "\x0f\xf0\x0f\xf0" \ 43 "\x00\x00\x01" 44 ORDER = "abcde" 45 46 def __init__(self, chunks={}): 47 self.chunks = dict(chunks) 48 49 def __getitem__(self, item): 50 return self.chunks[item] 51 def __setitem__(self, item, value): 52 self.chunks[item] = value 53 def __delitem__(self, item): 54 del self.chunks[item] 55 56 def chunkorder(self, (tag, value)): 57 if tag in self.ORDER: 58 return self.ORDER.index(tag) 59 return 256 + ord(tag) 60 61 @classmethod 62 def fromstring(cls, data): 63 if not data.startswith(cls.MAGIC): 64 raise ValueError, "data does not start with the magic number" 65 i = len(cls.MAGIC) 66 chunks = {} 67 while i < len(data): 68 tag = data[i] 69 if tag in cls.SMALLCHUNKS: 70 chunksize = struct.unpack(">H", data[i+1:i+3])[0] 71 i = i + 3 72 else: 73 chunksize = struct.unpack(">I", data[i+1:i+5])[0] 74 i = i + 5 75 chunkdata = data[i:i+chunksize] 76 if tag in chunks: 77 raise ValueError, "bitfile has chunk %r more than once" % tag 78 chunks[tag] = chunkdata 79 i = i + chunksize 80 return cls(chunks) 81 82 @classmethod 83 def fromfile(cls, file): 84 return cls.fromstring(file.read()) 85 86 @classmethod 87 def fromfilename(cls, filename): 88 return cls.fromstring(open(filename, "rb").read()) 89 90 def tostring(self): 91 result = self.MAGIC 92 for tag, chunkdata in sorted(self.chunks.items(), key=self.chunkorder): 93 if len(tag) != 1: 94 raise ValueError, "Tag %r must be a length-1 string" % tag 95 result = result + tag 96 if tag in self.SMALLCHUNKS: 97 result += struct.pack(">H", len(chunkdata)) 98 else: 99 result += struct.pack(">I", len(chunkdata)) 100 result += chunkdata 101 102 return result 103 104 def tofile(self, file): 105 return file.write(self.tostring()) 106 107 def tofilename(self, filename): 108 return open(filename, "wb").write(self.tostring()) 109 110 if __name__ == '__main__': 111 import sys 112 113 if len(sys.argv) < 2: 114 c = BitFile() 115 c['a'] = "hello world" 116 c['e'] = "goodbye" 117 c['A'] = "shazam" 118 s = c.tostring() 119 print repr(s) 120 d = BitFile.fromstring(s) 121 print d.chunks 122 assert d.tostring() == s 123 124 for bitfile in sys.argv[1:]: 125 bits = open(bitfile, "rb").read() 126 c = BitFile.fromstring(bits) 127 for k, v in sorted(c.chunks.items()): 128 if k in 'abcd': 129 print k, v 130 else: 131 print k, len(v) 132 newbits = c.tostring() 133 assert bits == newbits # Assuming the original is in canonical order! 134 print