/ lib / python / bitfile.py
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