Adds opcodes for "unimp" and "c.unimp" unimplemented instructions.

These instructions will still generate invalid instruction exceptions,
but will not create log messages, as these are considered intentional
unimplemented instructions.

Also fixes an issue w.r.t. clearing and seeing status of breakpoints
on control-flow, interrupts, and exceptions for mpact-cheriot.

PiperOrigin-RevId: 720727822
Change-Id: Ica05e37fc634213f092a55c46c1004b2d922f509
diff --git a/cheriot/cheriot_cli_forwarder.cc b/cheriot/cheriot_cli_forwarder.cc
index 6c5075b..7f94943 100644
--- a/cheriot/cheriot_cli_forwarder.cc
+++ b/cheriot/cheriot_cli_forwarder.cc
@@ -78,6 +78,10 @@
   cheriot_cli_top_->CLISetBreakOnControlFlowChange(value);
 }
 
+bool CheriotCLIForwarder::BreakOnControlFlowChange() {
+  return cheriot_cli_top_->CLIBreakOnControlFlowChange();
+}
+
 absl::Status CheriotCLIForwarder::Halt() { return cheriot_cli_top_->CLIHalt(); }
 
 absl::Status CheriotCLIForwarder::Halt(HaltReason halt_reason) {
diff --git a/cheriot/cheriot_cli_forwarder.h b/cheriot/cheriot_cli_forwarder.h
index 3da7fb6..c7b700b 100644
--- a/cheriot/cheriot_cli_forwarder.h
+++ b/cheriot/cheriot_cli_forwarder.h
@@ -69,6 +69,7 @@
   absl::Status DisableAction(uint64_t address, int id) override;
   // Enable breaking on control flow change.
   void SetBreakOnControlFlowChange(bool value) override;
+  bool BreakOnControlFlowChange() override;
   // Request that core stop running.
   absl::Status Halt() override;
   absl::Status Halt(HaltReason halt_reason) override;
diff --git a/cheriot/cheriot_debug_interface.h b/cheriot/cheriot_debug_interface.h
index ae46629..0be9869 100644
--- a/cheriot/cheriot_debug_interface.h
+++ b/cheriot/cheriot_debug_interface.h
@@ -57,6 +57,7 @@
 
   // Enable/disable breaking on control flow change.
   virtual void SetBreakOnControlFlowChange(bool enabled) = 0;
+  virtual bool BreakOnControlFlowChange() = 0;
 };
 
 }  // namespace mpact::sim::cheriot
diff --git a/cheriot/cheriot_renode_cli_top.cc b/cheriot/cheriot_renode_cli_top.cc
index 2c7e20d..32ebab0 100644
--- a/cheriot/cheriot_renode_cli_top.cc
+++ b/cheriot/cheriot_renode_cli_top.cc
@@ -65,6 +65,11 @@
       [this, value]() { cheriot_top_->SetBreakOnControlFlowChange(value); });
 }
 
+bool CheriotRenodeCLITop::CLIBreakOnControlFlowChange() {
+  return DoWhenInControl<bool>(
+      [this]() -> bool { return cheriot_top_->BreakOnControlFlowChange(); });
+}
+
 absl::StatusOr<int> CheriotRenodeCLITop::CLISetActionPoint(
     uint64_t address, absl::AnyInvocable<void(uint64_t, int)> action) {
   return DoWhenInControl<absl::StatusOr<int>>([this, address, &action]() {
diff --git a/cheriot/cheriot_renode_cli_top.h b/cheriot/cheriot_renode_cli_top.h
index a806c4e..0aa4320 100644
--- a/cheriot/cheriot_renode_cli_top.h
+++ b/cheriot/cheriot_renode_cli_top.h
@@ -44,6 +44,7 @@
                                     AccessType access_type);
   absl::Status CLIClearDataWatchpoint(uint64_t address, AccessType access_type);
   void CLISetBreakOnControlFlowChange(bool value);
+  bool CLIBreakOnControlFlowChange();
 
   absl::StatusOr<int> CLISetActionPoint(
       uint64_t address, absl::AnyInvocable<void(uint64_t, int)> action);
diff --git a/cheriot/cheriot_top.h b/cheriot/cheriot_top.h
index 131f531..ea3ed27 100644
--- a/cheriot/cheriot_top.h
+++ b/cheriot/cheriot_top.h
@@ -118,6 +118,9 @@
   absl::Status ClearDataWatchpoint(uint64_t address,
                                    AccessType access_type) override;
   void SetBreakOnControlFlowChange(bool value) override;
+  bool BreakOnControlFlowChange() override {
+    return break_on_control_flow_change_;
+  }
 
   // If successful, returns a pointer to the instruction at the given address.
   // The instruction object is IncRef'ed, and the caller must DecRef the object
diff --git a/cheriot/debug_command_shell.cc b/cheriot/debug_command_shell.cc
index d75b384..a76aa82 100644
--- a/cheriot/debug_command_shell.cc
+++ b/cheriot/debug_command_shell.cc
@@ -651,6 +651,8 @@
       auto *cheriot_interface = reinterpret_cast<CheriotDebugInterface *>(
           core_access_[current_core_].debug_interface);
       cheriot_interface->SetBreakOnControlFlowChange(false);
+      interrupt_listeners_[current_core_]->SetEnableExceptions(false);
+      interrupt_listeners_[current_core_]->SetEnableInterrupts(false);
       auto result =
           core_access_[current_core_].debug_interface->ClearAllSwBreakpoints();
       if (!result.ok()) {
@@ -710,6 +712,8 @@
     // break list
     if (RE2::FullMatch(line_view, *list_break_re_)) {
       std::string bp_list;
+      absl::StrAppend(&bp_list, " index    active      address   symbol\n");
+      absl::StrAppend(&bp_list, " -------------------------------------\n");
       for (auto [index, address] : core_access_[current_core_].breakpoint_map) {
         bool active =
             core_access_[current_core_].debug_interface->HasBreakpoint(address);
@@ -720,11 +724,28 @@
           if (res.ok()) symbol = std::move(res.value());
         }
         absl::StrAppend(&bp_list,
-                        absl::StrFormat("  %3d   %-8s   0x%08x   %s\n", index,
+                        absl::StrFormat("  %3d   %8s   0x%08x   %s\n", index,
                                         active ? "active" : "inactive", address,
                                         symbol.empty() ? "-" : symbol));
       }
-      os << absl::StrCat("Breakpoints:\n", bp_list, "\n");
+      auto *cheriot_interface = reinterpret_cast<CheriotDebugInterface *>(
+          core_access_[current_core_].debug_interface);
+      if (cheriot_interface->BreakOnControlFlowChange()) {
+        absl::StrAppend(&bp_list,
+                        absl::StrFormat("        %8s   0x%08s   %s\n", "active",
+                                        "--------", "$branch"));
+      }
+      if (interrupt_listeners_[current_core_]->AreExceptionsEnabled()) {
+        absl::StrAppend(&bp_list,
+                        absl::StrFormat("        %8s   0x%08s   %s\n", "active",
+                                        "--------", "$exception"));
+      }
+      if (interrupt_listeners_[current_core_]->AreInterruptsEnabled()) {
+        absl::StrAppend(&bp_list,
+                        absl::StrFormat("        %8s   0x%08s   %s\n", "active",
+                                        "--------", "$interrupt"));
+      }
+      os << bp_list << std::endl;
       continue;
     }
 
diff --git a/cheriot/riscv_cheriot.bin_fmt b/cheriot/riscv_cheriot.bin_fmt
index 9a9fdf2..249f80c 100644
--- a/cheriot/riscv_cheriot.bin_fmt
+++ b/cheriot/riscv_cheriot.bin_fmt
@@ -297,6 +297,7 @@
   csrrwi_nr: I2Type : func3 == 0b101, rd == 0,  opcode == 0b111'0011;
   csrrsi_nw: I2Type : func3 == 0b110, i_uimm5 == 0, opcode == 0b111'0011;
   csrrci_nw: I2Type : func3 == 0b111, i_uimm5 == 0, opcode == 0b111'0011;
+  unimp    : IType  : func3 == 0b001, rs1 == 0, rd == 0, opcode == 0b111'0011;
   // RiscV32 Privileged instructions.
   mret    : Inst32Format  : bits == 0b001'1000'00010'00000'000'00000, opcode == 0b111'0011;
   wfi     : Inst32Format  : bits == 0b000'1000'00101'00000'000'00000, opcode == 0b111'0011;
@@ -492,4 +493,5 @@
   cheriot_cjr       : CR : func4 == 0b1000, rs1 > 1, rs2 == 0, op == 0b10;
   cheriot_cjr_cra   : CR : func4 == 0b1000, rs1 == 1, rs2 == 0, op == 0b10;
   cheriot_cjalr_cra : CR : func4 == 0b1001, rs1 != 0, rs2 == 0, op == 0b10;
+  cunimp : Inst16Format : func3 == 0b000, bits == 0b0'00000'00000, op == 0b00;
 };
diff --git a/cheriot/riscv_cheriot.isa b/cheriot/riscv_cheriot.isa
index 1af2abe..469a6c3 100644
--- a/cheriot/riscv_cheriot.isa
+++ b/cheriot/riscv_cheriot.isa
@@ -153,6 +153,9 @@
     ebreak{},
       disasm: "ebreak",
       semfunc: "&RiscVIEbreak";
+    unimp{},
+      disasm: "unimp",
+      semfunc: "&RiscVIUnimplemented";
   }
 }
 
@@ -598,6 +601,9 @@
     cebreak{},
       disasm: "c.ebreak",
       semfunc: "&RiscVIEbreak";
+    cunimp{},
+      disasm: "c.unimp",
+      semfunc: "&RiscVIUnimplemented";
   }
 }
 
diff --git a/cheriot/riscv_cheriot_i_instructions.cc b/cheriot/riscv_cheriot_i_instructions.cc
index 062879d..3baf3bf 100644
--- a/cheriot/riscv_cheriot_i_instructions.cc
+++ b/cheriot/riscv_cheriot_i_instructions.cc
@@ -242,6 +242,21 @@
   state->WFI(instruction);
 }
 
+void RiscVIUnimplemented(const Instruction *instruction) {
+  auto *state = static_cast<CheriotState *>(instruction->state());
+  // Get instruction word, as it needs to be used as trap value.
+  uint64_t address = instruction->address();
+  auto db = state->db_factory()->Allocate<uint32_t>(1);
+  state->DbgLoadMemory(address, db);
+  uint32_t inst_word = db->Get<uint32_t>(0);
+  db->DecRef();
+  // See if the instruction is interpreted as 32 or 16 bit instruction.
+  if ((inst_word & 0b11) != 0b11) inst_word &= 0xffff;
+  state->Trap(/*is_interrupt=*/false, /*trap_value=*/inst_word,
+              *EC::kIllegalInstruction,
+              /*epc=*/instruction->address(), instruction);
+}
+
 }  // namespace cheriot
 }  // namespace sim
 }  // namespace mpact
diff --git a/cheriot/riscv_cheriot_i_instructions.h b/cheriot/riscv_cheriot_i_instructions.h
index f5f7c4d..d24a61e 100644
--- a/cheriot/riscv_cheriot_i_instructions.h
+++ b/cheriot/riscv_cheriot_i_instructions.h
@@ -17,6 +17,8 @@
 #ifndef MPACT_CHERIOT__RISCV_CHERIOT_I_INSTRUCTIONS_H_
 #define MPACT_CHERIOT__RISCV_CHERIOT_I_INSTRUCTIONS_H_
 
+#include <cstdint>
+
 #include "mpact/sim/generic/instruction.h"
 
 // This file contains the declarations of the instruction semantic functions
@@ -90,6 +92,11 @@
 void RiscVISh(const Instruction *instruction);
 void RiscVISb(const Instruction *instruction);
 
+// Unimplemented instruction. This is executed when either the unimp or the
+// c.unimp instructions are issued. The instruction takes an unknown instruction
+// exception, but does not print a log message.
+// The instruction does not take any operands.
+void RiscVIUnimplemented(const Instruction *instruction);
 // The Fence instruction takes a single source operand (index 0) which consists
 // of an immediate value containing the right justified concatenation of the FM,
 // predecessor, and successor bit fields of the instruction.