[otbn] Add support for the "la" pseudo-operation to otbn-as

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/data/base-insns.yml b/hw/ip/otbn/data/base-insns.yml
index 24a531f..9d1d5ce 100644
--- a/hw/ip/otbn/data/base-insns.yml
+++ b/hw/ip/otbn/data/base-insns.yml
@@ -501,6 +501,15 @@
     followed by an ADDI).
   python-pseudo-op: true
 
+- mnemonic: la
+  synopsis: Load absolute address
+  rv32i: true
+  operands: [grd, imm]
+  doc: |
+    Loads an address given by a symbol into a GPR. This is represented
+    as a LUI and an ADDI.
+  python-pseudo-op: true
+
 - mnemonic: ret
   synopsis: Return from subroutine
   rv32i: true
diff --git a/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/bigla.exp b/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/bigla.exp
new file mode 100644
index 0000000..047706b
--- /dev/null
+++ b/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/bigla.exp
@@ -0,0 +1,6 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+x2 = 2048
+x3 = 0x1234
diff --git a/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/bigla.s b/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/bigla.s
new file mode 100644
index 0000000..15db594
--- /dev/null
+++ b/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/bigla.s
@@ -0,0 +1,16 @@
+/* Copyright lowRISC contributors. */
+/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */
+/* SPDX-License-Identifier: Apache-2.0 */
+/*
+    Checks for "la" pseudo-instruction support where the symbol is too big to
+    fit in a signed immediate for addi.
+*/
+.text
+  la x2, foo
+  lw x3, 0(x2)
+  ecall
+
+.data
+.org 2048
+foo:
+  .word 0x1234
diff --git a/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/la.exp b/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/la.exp
new file mode 100644
index 0000000..eba3151
--- /dev/null
+++ b/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/la.exp
@@ -0,0 +1,9 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+x2 = 0
+x3 = 4
+x4 = 0x1234
+x5 = 0x5678
+x6 = 0
diff --git a/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/la.s b/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/la.s
new file mode 100644
index 0000000..f95a710
--- /dev/null
+++ b/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/la.s
@@ -0,0 +1,19 @@
+/* Copyright lowRISC contributors. */
+/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */
+/* SPDX-License-Identifier: Apache-2.0 */
+/*
+    Checks for "la" pseudo-instruction support
+*/
+.data
+foo:
+  .word 0x1234
+
+bar:
+  .word 0x5678
+
+.text
+  la x2, foo
+  la x3, bar
+  lw x4, 0(x2)
+  lw x5, 0(x3)
+  ecall
diff --git a/hw/ip/otbn/dv/otbnsim/test/simple/insns/li.exp b/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/li.exp
similarity index 100%
rename from hw/ip/otbn/dv/otbnsim/test/simple/insns/li.exp
rename to hw/ip/otbn/dv/otbnsim/test/simple/pseudos/li.exp
diff --git a/hw/ip/otbn/dv/otbnsim/test/simple/insns/li.s b/hw/ip/otbn/dv/otbnsim/test/simple/pseudos/li.s
similarity index 100%
rename from hw/ip/otbn/dv/otbnsim/test/simple/insns/li.s
rename to hw/ip/otbn/dv/otbnsim/test/simple/pseudos/li.s
diff --git a/hw/ip/otbn/util/otbn-as b/hw/ip/otbn/util/otbn-as
index 9122884..5d8958e 100755
--- a/hw/ip/otbn/util/otbn-as
+++ b/hw/ip/otbn/util/otbn-as
@@ -455,19 +455,17 @@
     return (positionals, others, flags)
 
 
-def expand_li(where: str, op_to_expr: Dict[str, Optional[str]]) -> List[str]:
-    '''Expand the li pseudo-op'''
-
-    # This logic is slightly complicated so it has some associated tests in the
-    # ISS testsuite (where we can run the results). If adding more cleverness
-    # to this or fixing bugs, we should also add a check at
-    # hw/ip/otbn/dv/otbnsim/test/simple/li.
-
+def _unpack_lx(where: str,
+               mnemonic: str,
+               op_to_expr: Dict[str, Optional[str]]) -> Tuple[str, str]:
+    '''Unpack the arguments to li or la'''
     if set(op_to_expr.keys()) != {'grd', 'imm'}:
-        raise RuntimeError('When expanding LI, got wrong op_to_expr keys '
-                           '({}). This is a mismatch between expand_li in '
-                           'otbn-as and the operands for LI in insns.yml.'
-                           .format(list(op_to_expr.keys())))
+        umnem = mnemonic.upper()
+        keys_list = list(op_to_expr.keys())
+        raise RuntimeError(f'When expanding {umnem}, got wrong op_to_expr '
+                           f'keys ({keys_list}). This is a mismatch between '
+                           f'expand_{mnemonic} in otbn-as and the operands '
+                           f'for {umnem} in insns.yml.')
 
     grd = op_to_expr['grd']
     imm = op_to_expr['imm']
@@ -490,7 +488,18 @@
                            .format(where, err))
 
     grd_txt = gpr_type.op_val_to_str(grd_op_val, None)
+    return (grd_txt, imm)
 
+
+def expand_li(where: str, op_to_expr: Dict[str, Optional[str]]) -> List[str]:
+    '''Expand the li pseudo-op'''
+
+    # This logic is slightly complicated so it has some associated tests in the
+    # ISS testsuite (where we can run the results). If adding more cleverness
+    # to this or fixing bugs, we should also add a check at
+    # hw/ip/otbn/dv/otbnsim/test/simple/pseudos/li.s.
+
+    grd_txt, imm = _unpack_lx(where, 'li', op_to_expr)
     try:
         imm_int = int(imm, 0)
     except ValueError:
@@ -542,8 +551,30 @@
             'addi {}, {}, {}'.format(grd_txt, grd_txt, imm_12)]
 
 
+def expand_la(where: str, op_to_expr: Dict[str, Optional[str]]) -> List[str]:
+    '''Expand the la pseudo-op'''
+
+    # For RISC-V, "la rd, symbol" expands to two instructions:
+    #
+    #    auipc rd, delta[31:12] + delta[11]
+    #    addi  rd, rd, delta[11:0]
+    #
+    # where delta = symbol - pc.
+    #
+    # For OTBN, both IMEM and DMEM are small. This means that we can represent
+    # every symbol in 12 bits, so "la rd, symbol" can expand to:
+    #
+    #    addi rd, x0, %lo(symbol)
+    #
+    # Much easier!
+    grd_txt, imm = _unpack_lx(where, 'la', op_to_expr)
+    return ['lui {}, %hi({})'.format(grd_txt, imm),
+            'addi {}, {}, %lo({})'.format(grd_txt, grd_txt, imm)]
+
+
 _PSEUDO_OP_ASSEMBLERS = {
-    'li': expand_li
+    'li': expand_li,
+    'la': expand_la
 }