Skip to content

Commit

Permalink
Merge pull request YosysHQ#3 from daveshah1/docs2
Browse files Browse the repository at this point in the history
Add Bitstream Format documentation and master HTML generation script
  • Loading branch information
gatecat committed Apr 29, 2018
2 parents 53c0077 + e213ecb commit 7a0cac2
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 8 deletions.
108 changes: 102 additions & 6 deletions docs/architecture/bitstream_format.rst
Original file line number Diff line number Diff line change
@@ -1,12 +1,108 @@
Bitstream format
================

.. todo::
Expand on rough notes
Some documentation on the ECP5 bitstream format is published by Lattice themselves
in th ECP5 sysCONFIG Usage Guide (TN1260_).

.. _TN1260: http://www.latticesemi.com/~/media/LatticeSemi/Documents/ApplicationNotes/EH/TN1260.pdf

* Lattice BIT header
Basic Structure
----------------

* Additional information about how bitstream was generated
* A series of null-terminated strings, with FF 00 at the start and 00 FF at the end
* Is ignored entirely by devices
The ECP5 is primarily byte oriented and always byte aligned. Multi-byte words are always in big endian format.

Before the start of the bitstream itself is a comment section, which starts with FF 00 and 00 FF.
Inside it are several null-terminated strings used as metadata by Lattice. The start of the bitstream
is demarcated by a preamable, 0xFFFFBDB3. This is then followed by a 0xFFFFFFFF dummy section and then the
start of functional commands.

At minimum, a bitstream command is an 8 bit command type field, then 24 bits of command information.
The MSB of command information is set to 1 if a CRC16 follows the command. The other 23 bits are command-specific,
but usually zeros. Then follows a command-specific number of payload bytes, and the CRC16 if applicable.

The CRC16 is accumulated over all commands until a CRC16 check is reached. It is not reset at the end of commands
without a CRC16 check - except the ``LSC_RESET_CRC`` command, and after the actual bitstream payload
(``LSC_PROG_INCR_RTI``).

The CRC16 is calculated using the polynomial 0x8005 with no bit reversal. This algorithm is sometimes known as
"CRC16-BUYPASS".

NB: Lattice's documents talk about the CRC16 being flipped. This is based on standard
CRC16 code with reversal, effectively performing double bit reversal. CRC16 code with no
bit reversal at all matches the actual format.

Control Commands
------------------
Control commands seen in a typical uncompressed, unencrypted bitstream are:

+-------------------------------+-----+--------------------------+---------------------------------------------------+
| Command | Hex | Parameters | Description |
+==========================+====+=====+==========================+===================================================+
| Dummy | FF | N/A | Ignored, used for padding |
+-------------------------------+-----+--------------------------+---------------------------------------------------+
| ``LSC_RESET_CRC`` | 3B | 24 bit info: all 0 | Resets the CRC16 register |
+-------------------------------+-----+--------------------------+---------------------------------------------------+
| ``VERIFY_ID`` | E2 | - 24 bit info: all 0 | This checks the actual device ID against the given|
| | | - 32 bit device JTAG ID | value and fails if they do not match. |
+-------------------------------+-----+--------------------------+---------------------------------------------------+
| ``LSC_PROG_CNTRL0`` | 22 | - 24 bit info: all 0 | This sets the value of device control register 0 |
| | | - 32 bit CtlReg0 value | Normally 0x40000000 |
+-------------------------------+-----+--------------------------+---------------------------------------------------+
| ``LSC_INIT_ADDRESS`` | 46 | 24 bit info: all 0 | Resets the frame address register |
+-------------------------------+-----+--------------------------+---------------------------------------------------+
| ``ISC_PROGRAM_SECURITY`` | CE | 24 bit info: all 0 | Program the security bit (prevents readback (?) ) |
+-------------------------------+-----+--------------------------+---------------------------------------------------+
| ``ISC_PROGRAM_USERCODE`` | C2 | - CRC bit set, 23 bits 0 | Sets the value of the USERCODE register |
| | | - 32 bit USERCODE value | |
+-------------------------------+-----+--------------------------+---------------------------------------------------+
| ``ISC_PROGRAM_DONE`` | 5E | 24 bit info: all 0 | End of bitstream, set DONE high |
+-------------------------------+-----+--------------------------+---------------------------------------------------+

Configuration Data
-------------------
The FPGA configuration data itself is programmed by using command ``LSC_PROG_INCR_RTI`` (0x82). Following this command,
there are some setup bits:

- 1 bit: CRC16 comparison flag, normally set
- 1 bit: CRC16 comparison at end flag, normally cleared = CRC check after every frame
- 1 bit: dummy bits setting, normally cleared = include dummy bits in bitstream
- 1 bit: dummy byte setting, normally cleared = use following bits as number of dummy bytes
- 4 bits: number of dummy bytes between frames, usually 1
- 16 bits: number of configuration frames

This is then followed by a number of frames, each in the following format:

- The configuration frame itself, such that bit 0 of the first byte sent is the MSB of the frame,
bit 7 of the first byte the MSB-7 and bit 0 of the last byte (if there are no dummy bits) being the LSB of the frame.
- Any dummy bits needed to pad the frame to a whole number of bytes
- A CRC-16 checksum:

- For the first frame, this also covers any other commands sent
before the programming command but after a CRC reset, and the programming command itself.
- For subsequent frames, this excludes dummy bytes between frames
- Dummy 0xFF bytes, usually only 1

The highest numbered frame in the chip is sent first.

Separate commands are used if EBR needs to be configured in the bitstream. ``EBR_ADDRESS`` (0xF6) is used to select the
EBR to program and the starting address in the EBR; and ``LSC_EBR_WRITE`` (0xB2) is used to program the EBR itself using
72-bit frames. The specifics of these still need to be documented.

Device-Specific Information
------------------------------

+-----------+-------------+--------+-----------------------+----------------------+
| Device | Device ID | Frames | Config Bits per Frame | Dummy Bits per Frame |
+===========+=============+========+=======================+======================+
| LFE5U-25 | 0x41111043 | 7562 | 592 | 0 |
+-----------+-------------+--------+-----------------------+----------------------+
| LFE5UM-25 | 0x01111043 | 7562 | 592 | 0 |
+-----------+-------------+--------+-----------------------+----------------------+
| LFE5U-45 | 0x41112043 | 9470 | 846 | 2 |
+-----------+-------------+--------+-----------------------+----------------------+
| LFE5UM-45 | 0x01112043 | 9470 | 846 | 2 |
+-----------+-------------+--------+-----------------------+----------------------+
| LFE5U-85 | 0x41113043 | 13294 | 1136 | 0 |
+-----------+-------------+--------+-----------------------+----------------------+
| LFE5UM-85 | 0x01113043 | 13294 | 1136 | 0 |
+-----------+-------------+--------+-----------------------+----------------------+
22 changes: 22 additions & 0 deletions minitests/logic_to_global/l2g.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module global_test(input a, input b, output q);

wire clk = a&b /* synthesis COMP=slicex LOC="R45C2A" */;


reg reg_0 /* synthesis COMP=slice0 LOC="R2C2D" */;
reg reg_1 /* synthesis COMP=slice1 LOC="R2C124D" */;
reg reg_2 /* synthesis COMP=slice2 LOC="R93C124A" */;
reg reg_3 /* synthesis COMP=slice3 LOC="R93C2A" */;
reg reg_4;

always @(posedge clk) begin
reg_0 <= a;
reg_1 <= reg_0;
reg_2 <= reg_1;
reg_3 <= reg_2;
reg_4 <= reg_3;
end

assign q = reg_4;

endmodule
3 changes: 3 additions & 0 deletions minitests/lut/lut.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module top(input a, input b, input c, input d, output q);
assign q = a & b & c & d;
endmodule
2 changes: 2 additions & 0 deletions minitests/wire/wire_pad.lpf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
LOCATE COMP "a" SITE "C5" ; #PL11A
LOCATE COMP "q" SITE "C11" ; #PT36B
3 changes: 3 additions & 0 deletions minitests/wire/wire_pad.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module top(input a, output q);
assign q = a;
endmodule
6 changes: 6 additions & 0 deletions tools/common/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import os
from os import path
import json
import subprocess


def get_trellis_root():
"""Return the absolute path to the Project Trellis repo root"""
Expand Down Expand Up @@ -46,3 +48,7 @@ def get_tilegrid(family, device):
tgjson = path.join(get_db_subdir(family, device), "tilegrid.json")
with open(tgjson, "r") as f:
return json.load(f)


def get_db_commit():
return subprocess.getoutput('git -C "{}" rev-parse HEAD'.format(get_db_root()))
85 changes: 85 additions & 0 deletions tools/html_all.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""
Project Trellis Master HTML Generation Script
Usage:
html_all.py <output_folder>
"""

import os, sys, subprocess, time
from os import path
from string import Template
import argparse
from common import devices, database
import html_tilegrid

trellis_docs_index = """
<html>
<head>
<title>Project Trellis HTML Documentation</title>
</head>
<body>
<h1>Project Trellis HTML Documentation</h1>
<p>Project Trellis is a project to document the ECP5 bitstream and internal architecture.</p>
<p>This repository contains HTML documentation automatically generated from the
<a href="https://github.com/SymbiFlow/prjtrellis">Project Trellis</a> database. The equivalent
machine-readable data is located in <a href="https://github.com/SymbiFlow/prjtrellis-db">prjtrellis-db.<a/>
Currently only tilemap data is published. More information on routing and bitstream will be published in the future.
</p>
<p>More human-readable documentation on the ECP5 architecture and the Project Trellis methodology can be found
on the <a href="http://prjtrellis.readthedocs.io/en/latest/">Read the Docs</a> site.</p>
<p>This HTML documentation was generated at ${datetime} from prjtrellis-db commit
<a href="https://github.com/SymbiFlow/prjtrellis-db/tree/${commit}">${commit}</a>.</p>
<hr/>
$docs_toc
<hr/>
<p>Licensed under a very permissive <a href="COPYING">CC0 1.0 Universal</a> license.</p>
</body>
</html>
"""

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('fld', type=str,
help="output folder")


def generate_device_docs(family, device, folder):
html_tilegrid.main(["html_tilegrid", family, device, path.join(folder, "index.html")])


def main(argv):
args = parser.parse_args(argv[1:])
if not path.exists(args.fld):
os.mkdir(args.fld)
commit_hash = database.get_db_commit()
build_dt = time.strftime('%Y-%m-%d %H:%M:%S')
docs_toc = ""
for fam, fam_data in sorted(devices.families.items()):
fdir = path.join(args.fld, fam)
if not path.exists(fdir):
os.mkdir(fdir)
docs_toc += "<h3>{} Family</h3>".format(fam)
docs_toc += "<ul>"
for dev in fam_data["devices"]:
ddir = path.join(fdir, dev)
if not path.exists(ddir):
os.mkdir(ddir)
generate_device_docs(fam, dev, ddir)
docs_toc += '<li><a href="{}">{} Documentation</a></li>'.format(
'{}/{}/index.html'.format(fam, dev),
dev
)
docs_toc += "</ul>"
index_html = Template(trellis_docs_index).substitute(
datetime=build_dt,
commit=commit_hash,
docs_toc=docs_toc
)
with open(path.join(args.fld, "index.html"), 'w') as f:
f.write(index_html)


if __name__ == "__main__":
main(sys.argv)
8 changes: 6 additions & 2 deletions tools/html_tilegrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,12 @@ def main(argv):

f = args.outfile
print(
"<html><head><title>{} Tiles</title></head><body><table style='font-size: 8pt; border: 2px solid black; text-align: center'>".format(
args.device), file=f)
"""<html>
<head><title>{} Tiles</title></head>
<body>
<h1>{} Tilegrid</h1>
<table style='font-size: 8pt; border: 2px solid black; text-align: center'>
""".format(args.device, args.device), file=f)
for trow in tiles:
print("<tr style='height: 100px'>", file=f)
for tloc in trow:
Expand Down

0 comments on commit 7a0cac2

Please sign in to comment.