1 |
2 |
sinclairrf |
<html>
|
2 |
|
|
<title>
|
3 |
|
|
Memory Initialization
|
4 |
|
|
</title>
|
5 |
|
|
<body>
|
6 |
|
|
This file describes the contents of the memory intialization file and how to
|
7 |
|
|
use it for various vendor-specific tools.
|
8 |
|
|
<h1>Format</h1>
|
9 |
|
|
Each line of the file consists of a hex address into which the values are to
|
10 |
|
|
be stored and the corresponding value. For SSBCC.9x8 these are 9-bit
|
11 |
|
|
values.<br/><br/>
|
12 |
|
|
The format of each line is "<tt>@%04X %03X</tt>" where the 4-digit value is
|
13 |
|
|
the hex memory address and the 3-digit hex value is the 9-bit memory
|
14 |
|
|
value.<br/><br/>
|
15 |
|
|
<h1>Xilinx <tt>data2mem</tt></h1>
|
16 |
|
|
<tt>data2mem</tt> is a tool for modifying the block ram initialization
|
17 |
|
|
contents of bitstreams. Using this tool allows the micro controller assembly
|
18 |
|
|
code to be modified in the bitstream without rerunning the entire build
|
19 |
|
|
process.<br/><br/>
|
20 |
|
|
The following illustrates how to use <tt>data2mem</tt> using
|
21 |
|
|
ISE 14.5:<br/><br/>
|
22 |
|
|
<ol>
|
23 |
|
|
<li>Create a <tt>BMM</tt> file named "<tt>uc.bmm</tt>" for inclusion in the
|
24 |
|
|
build process:<br/><br/>
|
25 |
|
|
The file should look like the following. The text
|
26 |
|
|
"<tt>top_inst/uc_inst</tt>" needs to be modified to point to your
|
27 |
|
|
instantiation of the micro controller. Sometimes
|
28 |
|
|
"<tt>uc_inst/Mram_s_opcodeMemory</tt>" becomes
|
29 |
|
|
"<tt>uc_inst_Mram_s_opcodeMemory</tt>"<br/><br/>
|
30 |
|
|
<tt>ADDRESS_SPACE uc RAMB18 WORD_ADDRESSING [0x0:0x7FF]<br/>
|
31 |
|
|
BUS_BLOCK<br/>
|
32 |
|
|
top_inst/uc_inst/Mram_s_opcodeMemory [0:8];<br/>
|
33 |
|
|
END_BUS_BLOCK;<br/>
|
34 |
|
|
END_ADDRESS_SPACE;<br/></tt><br/>
|
35 |
|
|
The following command can be used to verify the syntax of this <tt>BMM</tt>
|
36 |
|
|
file:<br/><br/>
|
37 |
|
|
<tt> data2mem -bm uc_bmm<br/></tt><br/>
|
38 |
|
|
WARNING: Using "<tt>ARCHITECTURE 4096</tt>" on a Spartan-6 build
|
39 |
|
|
produced two RAMB16's and one RAMB8, not the expected two RAMB18's.
|
40 |
|
|
Changing the configuration command to "<tt>ARCHITECTURE 2048*2</tt>"
|
41 |
|
|
produced the desired results. The following <tt>BMM</tt> extracted the two
|
42 |
|
|
RAMB18 locations with the desired memory mapping:<br/><br/>
|
43 |
|
|
<tt>ADDRESS_SPACE uc RAMB18 WORD_ADDRESSING [0x0:0xFFF]<br/>
|
44 |
|
|
BUS_BLOCK<br/>
|
45 |
|
|
top_inst/uc_inst_Mram_s_opcodeMemory_0 [0:8];<br/>
|
46 |
|
|
END_BUS_BLOCK;<br/>
|
47 |
|
|
BUS_BLOCK<br/>
|
48 |
|
|
top_inst/uc_inst_Mram_s_opcodeMemory_1 [0:8];<br/>
|
49 |
|
|
END_BUS_BLOCK;<br/>
|
50 |
|
|
END_ADDRESS_SPACE;<br/></tt><br/>
|
51 |
9 |
sinclairrf |
Note: For a Spartan-3A the bit indices <tt>[0:8]</tt> may need to be reversed.<br/><br/>
|
52 |
2 |
sinclairrf |
<li>Add this file to the build process.<br/><br/>
|
53 |
|
|
For a command-line build this is done by adding "<tt>-bm uc.bmm</tt>"
|
54 |
|
|
to the argument list for ngdbuild.<br/><br/>
|
55 |
|
|
When <tt>bitgen</tt> is run it will create a file named "<tt>uc_bd.bmm</tt>
|
56 |
|
|
which will include the memory block address required to run
|
57 |
|
|
<tt>data2mem</tt>.<br/><br/>
|
58 |
|
|
<li>Perform the build and ensure that <tt>uc_bd.bmm</tt> has the address for
|
59 |
|
|
the memory block.<br/><br/>
|
60 |
|
|
<li>Run <tt>data2mem</tt> as follows, where "<tt>orig.bit</tt>" is the
|
61 |
|
|
assumed name for the original bitstream generated by Xilinx' tools:<br/><br/>
|
62 |
|
|
<tt>data2mem -bm uc_bd.bmm -bt orig.bit -bd uc.mem -o b new.bit;<br/></tt><br/>
|
63 |
|
|
<li>Compare the original bitstream to the modified bitstream as follows to
|
64 |
|
|
ensure this process worked.<br/><br/>
|
65 |
|
|
<tt>data2mem -bm uc.bmm -bt orig.bit -d > orig.dump;<br/>
|
66 |
|
|
data2mem -bm uc.bmm -bt new.bit -d > new.dump;<br/>
|
67 |
|
|
diff orig.dump new.dump | less;<br/></tt><br/>
|
68 |
|
|
The only differences other than file names and dates and such should be the
|
69 |
|
|
initialization values for the memory block.<br/><br/>
|
70 |
|
|
You can validate this process by using the original memory initialization
|
71 |
|
|
file, in which case the above differences should be limited to the file
|
72 |
|
|
name, data, etc., but not the memory contents.<br/><br/>
|
73 |
|
|
</ol>
|
74 |
|
|
If you didn't include the <tt>BMM</tt> file in the build process you can use
|
75 |
|
|
<tt>fpga_editor</tt> to get the memory names and memory locations. The
|
76 |
|
|
command to invoke it is:<br/><br/>
|
77 |
|
|
<tt> fpga_editor -r file.ncd file.pcf<br/></tt><br/>
|
78 |
|
|
Then, under "<tt>Name Filter</tt>" type "<tt>*opcodeMemory*</tt>" and hit
|
79 |
|
|
the <tt>ENTER</tt> key.
|
80 |
|
|
<h1>Xilinx <tt>Vivado</tt></h1>
|
81 |
|
|
As of this writing, Xilinx' Vivado does not have clean non-SDK support for
|
82 |
|
|
generating the files required to modify the processor instruction memory.
|
83 |
|
|
However, the TCL scripting language can be used for the following work-around
|
84 |
|
|
to this problem:
|
85 |
|
|
<ol>
|
86 |
|
|
<li>Determine the name of the memory.<br/><br/>
|
87 |
|
|
The following commands lists the names of all the block rams. This
|
88 |
|
|
obviously needs to be done after place and route.<br/><br/>
|
89 |
|
|
<tt>join [get_cells -hierarchical -filter { LOC =~ "RAMB*" }] "\n";<br/></tt><br/>
|
90 |
|
|
or<br/><br/>
|
91 |
|
|
<tt>join [filter [get_cells -hierarchical] { BEL =~ "RAMB*" }] "\n"<br/></tt><br/>
|
92 |
|
|
Alternatively, the following command lists the names of the block rams, the
|
93 |
|
|
type of the block ram, and their locations:<br/><br/>
|
94 |
|
|
<tt>foreach a [filter [get_cells -hierarchical] { BEL =~ "RAMB*" }] {
|
95 |
|
|
puts "$a [lindex [report_property -return_string $a BEL] 7] [lindex
|
96 |
|
|
[report_property -return_string $a LOC] 7]"; }<br/></tt><br/>
|
97 |
|
|
Any of these can be included in the build script or can be cut and pasted
|
98 |
|
|
into the TCL console in the GUI after place and route.<br/><br/>
|
99 |
|
|
Note: The "<tt>list_property_value</tt>" seems to be more natural to use
|
100 |
|
|
than the "<tt>[lindex ...</tt>" commands, but can only be used for
|
101 |
|
|
enumerated types, i.e., not for <tt>BEL</tt> and <tt>LOC</tt>
|
102 |
|
|
properties.<br/><br/>
|
103 |
|
|
</li>
|
104 |
|
|
<li>Once you've identified the name(s) of the memories, add the following
|
105 |
|
|
command to the build script or use it on a checkpoint. Here,
|
106 |
|
|
uc/inst/s_PC_reg_rep was the single memory in the micro controller.<br/><br/>
|
107 |
|
|
<tt>foreach memName [list "uc/inst/s_PC_reg_rep"] {<br/>
|
108 |
|
|
set memBel [lindex [report_property -return_string [get_cells $memName] BEL] 7];<br/>
|
109 |
|
|
set memLoc [lindex [report_property -return_string [get_cells $memName] LOC] 7];<br/>
|
110 |
|
|
puts "MYBMMINFO: $memName $memBel $memLoc]";<br/>
|
111 |
|
|
}<br/></tt><br/>
|
112 |
|
|
This should add lines starting with "<tt>MYBMMINFO:</tt>" to the Vivado log
|
113 |
|
|
file with each memory name, type, and location.<br/><br/>
|
114 |
|
|
Note: If the processor uses more than one block ram, simply append the name
|
115 |
|
|
to the "<tt>list</tt>" in this TCL script.<br/><br/>
|
116 |
|
|
</li>
|
117 |
|
|
<li>Use the following <tt>gawk</tt> script or similar to generate a
|
118 |
|
|
<tt>BMM</tt> file from the "<tt>MYBMMINFO:</tt>" lines. Here, the
|
119 |
|
|
"<tt>vivado.log</tt>" is the Vivado log file and "<tt>build-bmm</tt> is the
|
120 |
|
|
name of this file.<br/><br/>
|
121 |
|
|
<tt>#!/bin/bash<br/>
|
122 |
|
|
#<br/>
|
123 |
|
|
# Generate a BMM file for the micro controller from the MYBMMINFO lines in the<br/>
|
124 |
|
|
# Vivado log file.<br/>
|
125 |
|
|
#<br/>
|
126 |
|
|
# Usage: ./build-bmm<br/>
|
127 |
|
|
<br/>
|
128 |
|
|
gawk -- '<br/>
|
129 |
|
|
BEGIN {<br/>
|
130 |
|
|
nMemories = 0;<br/>
|
131 |
|
|
memName[nMemories++] = "uc/inst/s_PC_reg_rep";<br/>
|
132 |
|
|
}<br/>
|
133 |
|
|
/^MYBMMINFO:/ { bel[$2] = $3; loc[$2] = $4; }<br/>
|
134 |
|
|
END {<br/>
|
135 |
|
|
for (ix=0; ix<nMemories; ++ix)<br/>
|
136 |
|
|
if (!(memName[ix] in bel)) {<br/>
|
137 |
|
|
printf("FATAL ERROR: MYBMMINFO record not found for \"%s\"\n", memName) > "/dev/stderr";<br/>
|
138 |
|
|
exit 1;<br/>
|
139 |
|
|
}<br/>
|
140 |
|
|
memType = "";<br/>
|
141 |
|
|
for (ix=0; ix<nMemories; ++ix) {<br/>
|
142 |
|
|
split(loc[memName[ix]],splitLoc,"_");<br/>
|
143 |
|
|
if (memType == "") {<br/>
|
144 |
|
|
memType = splitLoc[1];<br/>
|
145 |
|
|
if (memType = "RAMB18") L = 2048;<br/>
|
146 |
|
|
if (memType = "RAMB36") L = 4096;<br/>
|
147 |
|
|
L *= nMemories;<br/>
|
148 |
|
|
printf("ADDRESS_SPACE uc %s WORD_ADDRESSING [0x0:0x%x]\n",memType,L-1);<br/>
|
149 |
|
|
}<br/>
|
150 |
|
|
else if (splitLoc[1] != memType) {<br/>
|
151 |
|
|
printf("Inconsistent memory types: %s is %s instead of %s\n",memName[ix],splitLoc[1],memType) > "/dev/stderr";<br/>
|
152 |
|
|
exit 1;<br/>
|
153 |
|
|
}<br/>
|
154 |
|
|
printf(" BUS_BLOCK\n");<br/>
|
155 |
|
|
printf(" %s [8:0] PLACED = %s;\n",memName[ix],splitLoc[2]);<br/>
|
156 |
|
|
printf(" END_BUS_BLOCK;\n");<br/>
|
157 |
|
|
}<br/>
|
158 |
|
|
printf("END_ADDRESS_SPACE;\n");<br/>
|
159 |
|
|
}<br/>
|
160 |
|
|
' vivado.log > build_uc.bmm<br/></tt><br/>
|
161 |
|
|
</li>
|
162 |
|
|
<li>Update the contents of the bitstream file as follows. Here,
|
163 |
|
|
<tt>build.bit</tt> is the original bitstream and <tt>build_uc.bit</tt> is
|
164 |
|
|
the bitstream updated with the new micro controller instructions.<br/><br/>
|
165 |
|
|
<tt>data2mem -bm build_uc.bmm -bt build.bit -bd uc/uc.mem -o b
|
166 |
|
|
build_uc.bit<br/></tt><br/>
|
167 |
|
|
Alternatively, use something like the following as an "<tt>update_uc</tt>"
|
168 |
|
|
script file:<br/><br/>
|
169 |
|
|
<tt>#!/bin/bash<br/>
|
170 |
|
|
#<br/>
|
171 |
|
|
# Update the micro controller instruction memory in the BIT file.<br/>
|
172 |
|
|
#<br/>
|
173 |
|
|
# Usage: ./update-uc<br/>
|
174 |
|
|
<br/>
|
175 |
|
|
if test ! -f build_uc.bmm -o vivado.log -nt build_uc.bmm; then<br/>
|
176 |
|
|
./build-bmm || { echo "build-bmm failed" > /dev/stderr; exit 1; }<br/>
|
177 |
|
|
fi<br/>
|
178 |
|
|
<br/>
|
179 |
|
|
( cd uc; ssbcc -q --define-clog2 uc.9x8 ) \<br/>
|
180 |
|
|
|| { echo "ssbcc failed" > /dev/stderr; exit 1; }<br/>
|
181 |
|
|
<br/>
|
182 |
|
|
data2mem -bm build_uc.bmm -bt build.bit -bd uc/uc.mem -o b build_uc.bit \<br/>
|
183 |
|
|
|| { echo "data2mem failed" > /dev/stderr; exit 1; }<br/></tt><br/>
|
184 |
|
|
</li>
|
185 |
|
|
</ol>
|
186 |
|
|
Note: If you want to use these procedures to identify the memories after
|
187 |
|
|
you've run Vivado, you must include a "<tt>write_checkpoint</tt>" command in
|
188 |
|
|
your TCL script. For example, include the following command after
|
189 |
|
|
"<tt>write_bitstream -force build.bit</tt>"<br/><br/>
|
190 |
|
|
<tt>write_checkpoint -force build;<br/></tt><br/>
|
191 |
|
|
and then use the following command in a subsequent Vivado session to open the
|
192 |
|
|
checkpoint:<br/><br/>
|
193 |
|
|
<tt>read_checkpoint build;<br/></tt><br/>
|
194 |
|
|
Once these are done you can examine the memory names and so forth.<br/><br/>
|
195 |
|
|
</body>
|
196 |
|
|
</html>
|