[otbn] Update M, L, Z flags from bitwise ops in simulator and docs

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/data/bignum-insns.yml b/hw/ip/otbn/data/bignum-insns.yml
index ccb3475..5d637b1 100644
--- a/hw/ip/otbn/data/bignum-insns.yml
+++ b/hw/ip/otbn/data/bignum-insns.yml
@@ -489,6 +489,7 @@
     Performs a bitwise and operation.
     Takes the values stored in registers referenced by `wrs1` and `wrs2` and stores the result in the register referenced by `wrd`.
     The content of the second source register can be shifted by an immediate before it is consumed by the operation.
+    The M, L and Z flags in flag group 0 are updated with the result of the operation.
   decode: &bn-and-decode |
     d = UInt(wrd)
     a = UInt(wrs1)
@@ -501,6 +502,8 @@
     result = a & b_shifted
 
     WDR[d] = result
+    flags_out = FlagsForResult(result)
+    FLAGS[0] = {M: flags_out.M, L: flags_out.L, Z: flags_out.Z, C: FLAGS[0].C}
   encoding:
     scheme: bna
     mapping:
@@ -520,12 +523,15 @@
     Performs a bitwise or operation.
     Takes the values stored in WDRs referenced by `wrs1` and `wrs2` and stores the result in the WDR referenced by `wrd`.
     The content of the second source WDR can be shifted by an immediate before it is consumed by the operation.
+    The M, L and Z flags in flag group 0 are updated with the result of the operation.
   decode: *bn-and-decode
   operation: |
     b_shifted = ShiftReg(b, st, sb)
     result = a | b_shifted
 
     WDR[d] = result
+    flags_out = FlagsForResult(result)
+    FLAGS[0] = {M: flags_out.M, L: flags_out.L, Z: flags_out.Z, C: FLAGS[0].C}
   encoding:
     scheme: bna
     mapping:
@@ -551,6 +557,7 @@
   doc: |
     Negates the value in `wrs` and stores the result in the register referenced by `wrd`.
     The source value can be shifted by an immediate before it is consumed by the operation.
+    The M, L and Z flags in flag group 0 are updated with the result of the operation.
   decode: |
     d = UInt(wrd)
     a = UInt(wrs1)
@@ -562,6 +569,8 @@
     result = ~a_shifted
 
     WDR[d] = result
+    flags_out = FlagsForResult(result)
+    FLAGS[0] = {M: flags_out.M, L: flags_out.L, Z: flags_out.Z, C: FLAGS[0].C}
   encoding:
     scheme: bna
     mapping:
@@ -581,12 +590,15 @@
     Performs a bitwise xor operation.
     Takes the values stored in WDRs referenced by `wrs1` and `wrs2` and stores the result in the WDR referenced by `wrd`.
     The content of the second source WDR can be shifted by an immediate before it is consumed by the operation.
+    The M, L and Z flags in flag group 0 are updated with the result of the operation.
   decode: *bn-and-decode
   operation: |
     b_shifted = ShiftReg(b, st, sb)
     result = a ^ b_shifted
 
     WDR[d] = result
+    flags_out = FlagsForResult(result)
+    FLAGS[0] = {M: flags_out.M, L: flags_out.L, Z: flags_out.Z, C: FLAGS[0].C}
   encoding:
     scheme: bnaf
     mapping:
diff --git a/hw/ip/otbn/dv/otbnsim/sim/insn.py b/hw/ip/otbn/dv/otbnsim/sim/insn.py
index 59d83c5..387de72 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/insn.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/insn.py
@@ -585,8 +585,10 @@
     def execute(self, state: OTBNState) -> None:
         b_shifted = ShiftReg(state.wreg[self.wrs2],
                              self.shift_type, self.shift_bytes)
-        a = state.wreg[self.wrs1]
-        state.wreg[self.wrd] = a & b_shifted
+        a = state.wreg[self.wrs1].unsigned()
+        result = a & b_shifted
+        state.wreg[self.wrd] = result
+        state.update_mlz_flags(0, result)
 
 
 class BNOR(OTBNInsn):
@@ -604,7 +606,9 @@
         b_shifted = ShiftReg(state.wreg[self.wrs2],
                              self.shift_type, self.shift_bytes)
         a = state.wreg[self.wrs1]
-        state.wreg[self.wrd] = a | b_shifted
+        result = a | b_shifted
+        state.wreg[self.wrd] = result
+        state.update_mlz_flags(0, result)
 
 
 class BNNOT(OTBNInsn):
@@ -620,7 +624,9 @@
     def execute(self, state: OTBNState) -> None:
         b_shifted = ShiftReg(state.wreg[self.wrs],
                              self.shift_type, self.shift_bytes)
-        state.wreg[self.wrd] = ~b_shifted
+        result = ~b_shifted
+        state.wreg[self.wrd] = result
+        state.update_mlz_flags(0, result)
 
 
 class BNXOR(OTBNInsn):
@@ -638,7 +644,9 @@
         b_shifted = ShiftReg(state.wreg[self.wrs2],
                              self.shift_type, self.shift_bytes)
         a = state.wreg[self.wrs1]
-        state.wreg[self.wrd] = a ^ b_shifted
+        result = a ^ b_shifted
+        state.wreg[self.wrd] = result
+        state.update_mlz_flags(0, result)
 
 
 class BNRSHI(OTBNInsn):
diff --git a/hw/ip/otbn/dv/otbnsim/sim/state.py b/hw/ip/otbn/dv/otbnsim/sim/state.py
index 2470862..86bdd3a 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/state.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/state.py
@@ -233,6 +233,19 @@
                 setattr(self, n, getattr(self._new_val, n))
         self._new_val = None
 
+    @staticmethod
+    def mlz_for_result(C: bool, result: int) -> 'FlagReg':
+        '''Generate flags for the result of an operation.
+
+        C is the value for the C flag. result is the wide-side result value
+        that is used to generate M, L and Z.
+
+        '''
+        M = bool((result >> 255) & 1)
+        L = bool(result & 1)
+        Z = bool(result == 0)
+        return FlagReg(C=C, M=M, L=L, Z=Z)
+
 
 class FlagGroups:
     def __init__(self) -> None:
@@ -389,14 +402,14 @@
     @staticmethod
     def add_with_carry(a: int, b: int, carry_in: int) -> Tuple[int, FlagReg]:
         result = a + b + carry_in
-
         carryless_result = result & ((1 << 256) - 1)
-        flags_out = FlagReg(C=bool((result >> 256) & 1),
-                            M=bool((result >> 255) & 1),
-                            L=bool(result & 1),
-                            Z=bool(1 if carryless_result == 0 else 0))
+        C = bool((result >> 256) & 1)
 
-        return (carryless_result, flags_out)
+        return (carryless_result, FlagReg.mlz_for_result(C, carryless_result))
+
+    def update_mlz_flags(self, fg: int, result: int) -> None:
+        '''Update M, L, Z flags for the given result'''
+        self.flags[fg] = FlagReg.mlz_for_result(self.flags[fg].C, result)
 
     def post_insn(self) -> None:
         '''Update state after running an instruction but before commit'''