[otbn] Properly generate multi-section output in otbn-rig
The code was trying to use the .offset assembler directive to tell it
where to put things, but this turns out not to work at all. So we now
generate a linker script as well as the assembly code.
The command line is pretty horrible, but will be tidied up
soon (splitting up "generate me some snippets" and "turn them into
assembly"). For now, you can run it as
./hw/ip/otbn/util/otbn-rig \
--asm-output gen.S \
--ld-output gen.ld \
-o gen.json
The resulting gen.S looks something like this:
/* Section 0 (addresses [0x0000..0x0007]) */
.section .text.sec0000
addi x30, x0, 1269
jal x6, 3320
/* Section 1 (addresses [0x02d4..0x0333]) */
.section .text.sec0001
lui x29, 29447
srl x18, x30, x30
slli x17, x24, 5
ori x16, x17, -1155
or x3, x16, x29
sub x25, x18, x29
or x6, x6, x0
lui x21, 273100
and x25, x24, x24
...
and gen.ld looks something like this:
SECTIONS
{
/* Section 0 (addresses [0x0000..0x0007]) */
.text.sec0000 0x0 : AT(0x100000)
{
*(.text.sec0000)
}
/* Section 1 (addresses [0x02d4..0x0333]) */
.text.sec0001 0x2d4 : AT(0x1002d4)
{
*(.text.sec0001)
}
...
Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/util/otbn-rig b/hw/ip/otbn/util/otbn-rig
index 0ea366d..5c52d87 100755
--- a/hw/ip/otbn/util/otbn-rig
+++ b/hw/ip/otbn/util/otbn-rig
@@ -27,6 +27,8 @@
help='Path for JSON output of generated snippets')
parser.add_argument('--asm-output',
help='Optional path for generated program')
+ parser.add_argument('--ld-output',
+ help='Optional path for generated linker script')
parser.add_argument('--seed', type=int, default=0,
help='Random seed. Defaults to 0.')
parser.add_argument('--size', type=int, default=100,
@@ -69,6 +71,16 @@
.format(args.asm_output, err))
return 1
+ # If a linker script was requested, dump that here
+ if args.ld_output is not None:
+ try:
+ with open(args.ld_output, 'w') as out_file:
+ program.dump_linker_script(out_file)
+ except OSError as err:
+ sys.stderr.write('Failed to open ld script output file {!r}: {}.'
+ .format(args.ld_output, err))
+ return 1
+
return 0
diff --git a/hw/ip/otbn/util/rig/program.py b/hw/ip/otbn/util/rig/program.py
index 1771228..15db1b4 100644
--- a/hw/ip/otbn/util/rig/program.py
+++ b/hw/ip/otbn/util/rig/program.py
@@ -65,8 +65,9 @@
# instructions for the section.
_SecData = Tuple[int, int, List[ProgInsn]]
- def __init__(self, imem_size: int) -> None:
+ def __init__(self, imem_lma: int, imem_size: int) -> None:
assert imem_size & 3 == 0
+ self.imem_lma = imem_lma
self.imem_size = imem_size
# A map from base address (VMA) to a list of instructions. Each
@@ -149,15 +150,22 @@
assert self._cur_section is not None
self._cur_section[1].add_insns(insns)
+ @staticmethod
+ def _get_section_comment(idx: int,
+ addr: int,
+ insns: List[ProgInsn]) -> str:
+ return ('/* Section {} (addresses [{:#06x}..{:#06x}]) */'
+ .format(idx, addr, addr + 4 * len(insns) - 1))
+
def dump_asm(self, out_file: TextIO) -> None:
'''Write an assembly representation of the program to out_file'''
# Close any existing section, so that we can iterate over all the
# instructions by iterating over self._sections.
self.close_section()
for idx, (addr, insns) in enumerate(sorted(self._sections.items())):
- out_file.write('{}/* Section {} ({} instructions) */\n'
- .format('\n' if idx else '', idx, len(insns)))
- out_file.write('.offset {:#x}\n'.format(addr))
+ comment = Program._get_section_comment(idx, addr, insns)
+ out_file.write('{}{}\n'.format('\n' if idx else '', comment))
+ out_file.write('.section .text.sec{:04}\n'.format(idx))
for pi in insns:
insn = pi.insn
# We should never try to generate an instruction without syntax
@@ -182,6 +190,27 @@
out_file.write('{:14}{}\n'.format(mnem, rendered_ops))
+ def dump_linker_script(self, out_file: TextIO) -> None:
+ '''Write a linker script to link the program
+
+ This lays out the sections generated in dump_asm().
+
+ '''
+ self.close_section()
+ out_file.write('SECTIONS\n'
+ '{\n')
+ for idx, (addr, insns) in enumerate(sorted(self._sections.items())):
+ comment = Program._get_section_comment(idx, addr, insns)
+ out_file.write('{} {}\n'.format('\n' if idx else '', comment))
+ sec_name = '.text.sec{:04}'.format(idx)
+ lma = addr + self.imem_lma
+ out_file.write(' {} {:#x} : AT({:#x})\n'
+ ' {{\n'
+ ' *({})\n'
+ ' }}\n'
+ .format(sec_name, addr, lma, sec_name))
+ out_file.write('}\n')
+
def pick_branch_targets(self,
min_len: int,
count: int,
diff --git a/hw/ip/otbn/util/rig/rig.py b/hw/ip/otbn/util/rig/rig.py
index 684635b..aca1edf 100644
--- a/hw/ip/otbn/util/rig/rig.py
+++ b/hw/ip/otbn/util/rig/rig.py
@@ -31,13 +31,13 @@
# at address 0: a strict Harvard architecture. (mems[x][0] is the LMA
# for memory x, not the VMA)
mems = get_memory_layout()
- imem_size = mems['IMEM'][1]
+ imem_lma, imem_size = mems['IMEM']
dmem_size = mems['DMEM'][1]
assert start_addr <= imem_size - 4
assert start_addr & 3 == 0
- program = Program(imem_size)
+ program = Program(imem_lma, imem_size)
model = Model(dmem_size, start_addr)
generators = SnippetGens(insns_file)