From f4470f54f0b85311f1daecfd7bcae4a541d774a1 Mon Sep 17 00:00:00 2001 From: "Paul A. Clarke" Date: Thu, 6 Nov 2025 09:06:58 -0600 Subject: [PATCH 1/6] feat(data): add Zcmt instructions with IDL Signed-off-by: Paul A. Clarke --- spec/std/isa/inst/Zcmt/cm.jalt.yaml | 67 +++++++++++++++++++++++++++++ spec/std/isa/inst/Zcmt/cm.jt.yaml | 61 ++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 spec/std/isa/inst/Zcmt/cm.jalt.yaml create mode 100644 spec/std/isa/inst/Zcmt/cm.jt.yaml diff --git a/spec/std/isa/inst/Zcmt/cm.jalt.yaml b/spec/std/isa/inst/Zcmt/cm.jalt.yaml new file mode 100644 index 000000000..e37411f51 --- /dev/null +++ b/spec/std/isa/inst/Zcmt/cm.jalt.yaml @@ -0,0 +1,67 @@ +# Copyright (c) Ventana Micro Systems +# SPDX-License-Identifier: BSD-3-Clause-Clear + +# yaml-language-server: $schema=../../../../schemas/inst_schema.json + +$schema: "inst_schema.json#" +kind: instruction +name: cm.jalt +long_name: Jump Via Table with Optional Link +description: | + Read an address from the Jump Vector Table and jump to it, linking to `ra`. +definedBy: Zcmt +assembly: index +encoding: + match: 101000--------10 + variables: + - name: index + location: 9-2 + # prettier-ignore + not: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 ] +access: + s: always + u: always + vs: always + vu: always +operation(): | + # Ensure JVT readable + XReg jvt = csr_sw_read(0x017); + + if (CSR[jvt].MODE != 0) { + raise(ExceptionCode::IllegalInstruction, mode(), $encoding); + } + + # Skip over _this_ 16-bit instruction + XReg return_addr = $pc + 2; + X[1] = return_addr; + + XReg jump_table_base = { CSR[jvt].BASE, 6'b000000 }; + XReg virtual_address = jump_table_base + index * (xlen() / 8); + XReg addr; + + # TODO + # For a table jump instruction, the table entry that the instruction selects + # is considered an extension of the instruction itself. Hence, the execution + # of a table jump instruction involves two instruction fetches, the first to + # read the instruction (cm.jt/cm.jalt) and the second to read from the jump + # vector table (JVT). Both instruction fetches are implicit reads, and both + # require execute permission; read permission is irrelevant. It is + # recommended that the second fetch be ignored for hardware triggers and breakpoints. + # + # If an exception occurs on either instruction fetch, xEPC is set to the PC + # of the table jump instruction, xCAUSE is set as expected for the type of + # fault and xTVAL (if not set to zero) contains the fetch address which + # caused the fault. + + if (xlen() == 32) { + addr = read_memory<32>(virtual_address, $encoding); + } else { + addr = read_memory<64>(virtual_address, $encoding); + } + + # Ensure low-order bit is clear + addr = addr & $signed(2'b10); + + jump(addr); + +sail(): | diff --git a/spec/std/isa/inst/Zcmt/cm.jt.yaml b/spec/std/isa/inst/Zcmt/cm.jt.yaml new file mode 100644 index 000000000..f843683a2 --- /dev/null +++ b/spec/std/isa/inst/Zcmt/cm.jt.yaml @@ -0,0 +1,61 @@ +# Copyright (c) Ventana Micro Systems +# SPDX-License-Identifier: BSD-3-Clause-Clear + +# yaml-language-server: $schema=../../../../schemas/inst_schema.json + +$schema: "inst_schema.json#" +kind: instruction +name: cm.jt +long_name: Jump Via Table +description: | + Read an address from the Jump Vector Table and jump to it. +definedBy: Zcmt +assembly: index +encoding: + match: 101000000-----10 + variables: + - name: index + location: 6-2 +access: + s: always + u: always + vs: always + vu: always +operation(): | + # Ensure JVT readable + XReg jvt = csr_sw_read(0x017); + + if (CSR[jvt].MODE != 0) { + raise(ExceptionCode::IllegalInstruction, mode(), $encoding); + } + + XReg jump_table_base = { CSR[jvt].BASE, 6'b000000 }; + XReg virtual_address = jump_table_base + index `* (xlen() / 8); + XReg addr; + + # TODO + # For a table jump instruction, the table entry that the instruction selects + # is considered an extension of the instruction itself. Hence, the execution + # of a table jump instruction involves two instruction fetches, the first to + # read the instruction (cm.jt/cm.jalt) and the second to read from the jump + # vector table (JVT). Both instruction fetches are implicit reads, and both + # require execute permission; read permission is irrelevant. It is + # recommended that the second fetch be ignored for hardware triggers and breakpoints. + # + # If an exception occurs on either instruction fetch, xEPC is set to the PC + # of the table jump instruction, xCAUSE is set as expected for the type of + # fault and xTVAL (if not set to zero) contains the fetch address which + # caused the fault. + + if (xlen() == 32) { + addr = read_memory<32>(virtual_address, $encoding); + } else { + addr = read_memory<64>(virtual_address, $encoding); + } + + # Ensure low-order bit is clear + addr = addr & $signed(2'b10); + + jump(addr); + +sail(): | From 81b1b2e14eec94212013b92c21b8ad1b93a54250 Mon Sep 17 00:00:00 2001 From: "Paul A. Clarke" Date: Thu, 6 Nov 2025 13:37:27 -0600 Subject: [PATCH 2/6] fix: update golden instruction appendix --- .../all_instructions.golden.adoc | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/backends/instructions_appendix/all_instructions.golden.adoc b/backends/instructions_appendix/all_instructions.golden.adoc index 347714277..1451a2a6d 100644 --- a/backends/instructions_appendix/all_instructions.golden.adoc +++ b/backends/instructions_appendix/all_instructions.golden.adoc @@ -6310,6 +6310,78 @@ Included in:: |=== +[#udb:doc:inst:cm_jalt] +== cm.jalt + +Synopsis:: +Jump Via Table with Optional Link + +Assembly:: +cm.jalt index + +Encoding:: +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +{"reg":[{"bits":2,"name": 0x2,"type":2},{"bits":8,"name": "index != {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}","type":4},{"bits":6,"name": 0x28,"type":2}]} +.... + +Description:: +Read an address from the Jump Vector Table and jump to it, linking to `ra`. + + +Decode Variables:: +[width="100%", cols="1,2", options="header"] +|=== +|Variable Name |Location +|index |$encoding[9:2] +|=== + +Included in:: +[options="autowrap,autowidth"] +|=== +| Extension | Version + +| *Zcmt* | ~> 1.0.0 + +|=== + + +[#udb:doc:inst:cm_jt] +== cm.jt + +Synopsis:: +Jump Via Table + +Assembly:: +cm.jt index + +Encoding:: +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +{"reg":[{"bits":2,"name": 0x2,"type":2},{"bits":5,"name": "index","type":4},{"bits":9,"name": 0x140,"type":2}]} +.... + +Description:: +Read an address from the Jump Vector Table and jump to it. + + +Decode Variables:: +[width="100%", cols="1,2", options="header"] +|=== +|Variable Name |Location +|index |$encoding[6:2] +|=== + +Included in:: +[options="autowrap,autowidth"] +|=== +| Extension | Version + +| *Zcmt* | ~> 1.0.0 + +|=== + + [#udb:doc:inst:cm_mva01s] == cm.mva01s From 1b8dd491f1c82c9f9031be37cfec7540bc4b085a Mon Sep 17 00:00:00 2001 From: "Paul A. Clarke" Date: Thu, 6 Nov 2025 14:25:23 -0600 Subject: [PATCH 3/6] fix: use csr_handles --- spec/std/isa/inst/Zcmt/cm.jalt.yaml | 3 ++- spec/std/isa/inst/Zcmt/cm.jt.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/std/isa/inst/Zcmt/cm.jalt.yaml b/spec/std/isa/inst/Zcmt/cm.jalt.yaml index e37411f51..701078c08 100644 --- a/spec/std/isa/inst/Zcmt/cm.jalt.yaml +++ b/spec/std/isa/inst/Zcmt/cm.jalt.yaml @@ -25,7 +25,8 @@ access: vu: always operation(): | # Ensure JVT readable - XReg jvt = csr_sw_read(0x017); + Csr csr_handle = direct_csr_lookup(0x017); + XReg jvt = csr_sw_read(csr_handle); if (CSR[jvt].MODE != 0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/spec/std/isa/inst/Zcmt/cm.jt.yaml b/spec/std/isa/inst/Zcmt/cm.jt.yaml index f843683a2..7a751061d 100644 --- a/spec/std/isa/inst/Zcmt/cm.jt.yaml +++ b/spec/std/isa/inst/Zcmt/cm.jt.yaml @@ -23,7 +23,8 @@ access: vu: always operation(): | # Ensure JVT readable - XReg jvt = csr_sw_read(0x017); + Csr csr_handle = direct_csr_lookup(0x017); + XReg jvt = csr_sw_read(csr_handle); if (CSR[jvt].MODE != 0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); From 9beb399e2feb4050c9c22d2c5b3f62a970078c84 Mon Sep 17 00:00:00 2001 From: "Paul A. Clarke" Date: Fri, 7 Nov 2025 11:47:33 -0600 Subject: [PATCH 4/6] fix: updates per review --- spec/std/isa/inst/Zcmt/cm.jalt.yaml | 34 +++++++++++++---------------- spec/std/isa/inst/Zcmt/cm.jt.yaml | 29 +++++++++++------------- 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/spec/std/isa/inst/Zcmt/cm.jalt.yaml b/spec/std/isa/inst/Zcmt/cm.jalt.yaml index 701078c08..1d48f3823 100644 --- a/spec/std/isa/inst/Zcmt/cm.jalt.yaml +++ b/spec/std/isa/inst/Zcmt/cm.jalt.yaml @@ -25,7 +25,7 @@ access: vu: always operation(): | # Ensure JVT readable - Csr csr_handle = direct_csr_lookup(0x017); + Csr csr_handle = direct_csr_lookup(CSR[jvt].address()); XReg jvt = csr_sw_read(csr_handle); if (CSR[jvt].MODE != 0) { @@ -37,30 +37,26 @@ operation(): | X[1] = return_addr; XReg jump_table_base = { CSR[jvt].BASE, 6'b000000 }; - XReg virtual_address = jump_table_base + index * (xlen() / 8); + XReg virtual_address = jump_table_base + index `* (xlen() / 8); XReg addr; + TranslationResult result; - # TODO - # For a table jump instruction, the table entry that the instruction selects - # is considered an extension of the instruction itself. Hence, the execution - # of a table jump instruction involves two instruction fetches, the first to - # read the instruction (cm.jt/cm.jalt) and the second to read from the jump - # vector table (JVT). Both instruction fetches are implicit reads, and both - # require execute permission; read permission is irrelevant. It is - # recommended that the second fetch be ignored for hardware triggers and breakpoints. - # - # If an exception occurs on either instruction fetch, xEPC is set to the PC - # of the table jump instruction, xCAUSE is set as expected for the type of - # fault and xTVAL (if not set to zero) contains the fetch address which - # caused the fault. + # TODO: Correct this check when we figure out what MISA can do + if (CSR[misa].S == 1) { + result = translate(virtual_address, MemoryOperation::Fetch, mode(), $encoding); + } else { + result.paddr = virtual_address; + } + + # may raise an exception + access_check(result.paddr, xlen(), $pc, MemoryOperation::Fetch, ExceptionCode::InstructionAccessFault, mode()); if (xlen() == 32) { - addr = read_memory<32>(virtual_address, $encoding); + addr = read_physical_memory<32>(result.paddr); } else { - addr = read_memory<64>(virtual_address, $encoding); - } + addr = read_physical_memory<64>(result.paddr); + } # Ensure low-order bit is clear - # Ensure low-order bit is clear addr = addr & $signed(2'b10); jump(addr); diff --git a/spec/std/isa/inst/Zcmt/cm.jt.yaml b/spec/std/isa/inst/Zcmt/cm.jt.yaml index 7a751061d..2ff6d9cf3 100644 --- a/spec/std/isa/inst/Zcmt/cm.jt.yaml +++ b/spec/std/isa/inst/Zcmt/cm.jt.yaml @@ -23,7 +23,7 @@ access: vu: always operation(): | # Ensure JVT readable - Csr csr_handle = direct_csr_lookup(0x017); + Csr csr_handle = direct_csr_lookup(CSR[jvt].address()); XReg jvt = csr_sw_read(csr_handle); if (CSR[jvt].MODE != 0) { @@ -33,25 +33,22 @@ operation(): | XReg jump_table_base = { CSR[jvt].BASE, 6'b000000 }; XReg virtual_address = jump_table_base + index `* (xlen() / 8); XReg addr; + TranslationResult result; - # TODO - # For a table jump instruction, the table entry that the instruction selects - # is considered an extension of the instruction itself. Hence, the execution - # of a table jump instruction involves two instruction fetches, the first to - # read the instruction (cm.jt/cm.jalt) and the second to read from the jump - # vector table (JVT). Both instruction fetches are implicit reads, and both - # require execute permission; read permission is irrelevant. It is - # recommended that the second fetch be ignored for hardware triggers and breakpoints. - # - # If an exception occurs on either instruction fetch, xEPC is set to the PC - # of the table jump instruction, xCAUSE is set as expected for the type of - # fault and xTVAL (if not set to zero) contains the fetch address which - # caused the fault. + # TODO: Correct this check when we figure out what MISA can do + if (CSR[misa].S == 1) { + result = translate(virtual_address, MemoryOperation::Fetch, mode(), $encoding); + } else { + result.paddr = virtual_address; + } + + # may raise an exception + access_check(result.paddr, xlen(), $pc, MemoryOperation::Fetch, ExceptionCode::InstructionAccessFault, mode()); if (xlen() == 32) { - addr = read_memory<32>(virtual_address, $encoding); + addr = read_physical_memory<32>(result.paddr); } else { - addr = read_memory<64>(virtual_address, $encoding); + addr = read_physical_memory<64>(result.paddr); } # Ensure low-order bit is clear From 01e908cde68789269362253b40b2c4ef7de23bae Mon Sep 17 00:00:00 2001 From: Derek Hower Date: Tue, 11 Nov 2025 07:40:40 -0800 Subject: [PATCH 5/6] fix(iss): make sure that CSR.address() generates as a bits type --- backends/cpp_hart_gen/lib/gen_cpp.rb | 4 ++++ backends/cpp_hart_gen/templates/csrs.hxx.erb | 5 ++++- tools/ruby-gems/idlc/lib/idlc/ast.rb | 5 ++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/backends/cpp_hart_gen/lib/gen_cpp.rb b/backends/cpp_hart_gen/lib/gen_cpp.rb index 9fb62c6b7..dd2ba3569 100644 --- a/backends/cpp_hart_gen/lib/gen_cpp.rb +++ b/backends/cpp_hart_gen/lib/gen_cpp.rb @@ -270,6 +270,8 @@ def gen_cpp(symtab, indent = 2, indent_spaces: 2) if csr_obj.nil? if function_name == "sw_read" "#{' ' * indent}__UDB_CSR_BY_ADDR(#{csr.idx_expr.gen_cpp(symtab, 0, indent_spaces:)}).#{function_name}(__UDB_XLEN)" + elsif function_name == "address" + "#{' ' * indent}__UDB_CSR_BY_ADDR(#{csr.idx_expr.gen_cpp(symtab, 0, indent_spaces:)}).address_bits()" else "#{' ' * indent}__UDB_CSR_BY_ADDR(#{csr.idx_expr.gen_cpp(symtab, 0, indent_spaces:)}).#{function_name.gsub('?', '_Q_')}(#{args_cpp.join(', ')})" end @@ -280,6 +282,8 @@ def gen_cpp(symtab, indent = 2, indent_spaces: 2) else "#{' ' * indent}__UDB_CSR_BY_NAME(#{csr_obj.name})._#{function_name}()" end + elsif function_name == "address" + "#{' ' * indent}__UDB_CSR_BY_NAME(#{csr_obj.name})._#{function_name}()" else "#{' ' * indent}__UDB_CSR_BY_NAME(#{csr_obj.name}).#{function_name.gsub('?', '_Q_')}(#{args_cpp.join(', ')})" end diff --git a/backends/cpp_hart_gen/templates/csrs.hxx.erb b/backends/cpp_hart_gen/templates/csrs.hxx.erb index 61f05f03c..76361ec86 100644 --- a/backends/cpp_hart_gen/templates/csrs.hxx.erb +++ b/backends/cpp_hart_gen/templates/csrs.hxx.erb @@ -207,9 +207,12 @@ namespace udb { CsrAddressType address_type() const override { return <%= csr.indirect? ? 'CsrAddressType::Indirect' : 'CsrAddressType::Direct' -%>; } unsigned address() const override { <%= !csr.indirect? ? "return #{csr.address};" : "throw CsrAddressTypeError(\"#{csr.name} is not direct addressible.\")" %>; } + _Bits<12, false> address_bits() const { <%= !csr.indirect? ? "return #{csr.address}_b;" : "throw CsrAddressTypeError(\"#{csr.name} is not direct addressible.\")" %>; } uint64_t indirect_address() const override { <%= csr.indirect? ? "return #{csr.indirect_address};" : "throw CsrAddressTypeError(\"#{csr.name} is not indirect.\")" %>; } uint8_t indirect_slot() const override { <%= csr.indirect? ? "return #{csr.indirect_slot};" : "throw CsrAddressTypeError(\"#{csr.name} is not indirect.\")" %>; } - static constexpr unsigned _address() { return <%= csr.address %>; } + <%- if !csr.indirect? -%> + static constexpr _Bits<12, false> _address() { return <%= csr.address %>_b; } + <%- end -%> const std::string name() const override { return "<%= csr.name %>"; } PrivilegeMode mode() const override { return PrivilegeMode::<%= csr.priv_mode %>; } bool writable() const override { return <%= csr.writable %>; } diff --git a/tools/ruby-gems/idlc/lib/idlc/ast.rb b/tools/ruby-gems/idlc/lib/idlc/ast.rb index ea179764b..87abc62ab 100644 --- a/tools/ruby-gems/idlc/lib/idlc/ast.rb +++ b/tools/ruby-gems/idlc/lib/idlc/ast.rb @@ -7445,8 +7445,11 @@ def initialize(input, interval, function_name, csr, args) def type_check(symtab) csr.type_check(symtab) - if ["sw_read", "address"].include?(function_name) + if function_name == "sw_read" type_error "unexpected argument(s)" unless args.empty? + elsif function_name == "address" + type_error "unexpected argument(s)" unless args.empty? + type_error "csr is accessed indirectly, and has no address" if csr_def(symtab)&.indirect? elsif ["implemented_without?"].include?(function_name) type_error "Expecting one argument" unless args.size == 1 type_error "Expecting an ExtensionName" unless args[0].type(symtab).kind == :enum_ref && args[0].class_name == "ExtensionName" From a14b6a96f9d49137c28f80d6576a05a44e4b68b0 Mon Sep 17 00:00:00 2001 From: Derek Hower Date: Tue, 11 Nov 2025 08:40:15 -0800 Subject: [PATCH 6/6] fix(idl): parser was not allowing bits casts on csr function calls --- spec/std/isa/inst/Zcmt/cm.jalt.yaml | 2 +- tools/ruby-gems/idlc/lib/idlc/ast.rb | 2 +- tools/ruby-gems/idlc/lib/idlc/idl.treetop | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/spec/std/isa/inst/Zcmt/cm.jalt.yaml b/spec/std/isa/inst/Zcmt/cm.jalt.yaml index 1d48f3823..3330e56ce 100644 --- a/spec/std/isa/inst/Zcmt/cm.jalt.yaml +++ b/spec/std/isa/inst/Zcmt/cm.jalt.yaml @@ -25,7 +25,7 @@ access: vu: always operation(): | # Ensure JVT readable - Csr csr_handle = direct_csr_lookup(CSR[jvt].address()); + Csr csr_handle = direct_csr_lookup($bits(CSR[jvt].address())); XReg jvt = csr_sw_read(csr_handle); if (CSR[jvt].MODE != 0) { diff --git a/tools/ruby-gems/idlc/lib/idlc/ast.rb b/tools/ruby-gems/idlc/lib/idlc/ast.rb index 87abc62ab..f82901015 100644 --- a/tools/ruby-gems/idlc/lib/idlc/ast.rb +++ b/tools/ruby-gems/idlc/lib/idlc/ast.rb @@ -2463,7 +2463,7 @@ def type(symtab) if symtab.mxlen == 64 && symtab.multi_xlen? Type.new(:bits, width: [field(symtab).location(32).size, field(symtab).location(64).size].max) else - Type.new(:bits, width: field(symtab).location(symtab.mxlen).size) + Type.new(:bits, width: field(symtab).location(symtab.mxlen.nil? ? 64 : symtab.mxlen).size) end elsif field(symtab).base64_only? Type.new(:bits, width: field(symtab).location(64).size) diff --git a/tools/ruby-gems/idlc/lib/idlc/idl.treetop b/tools/ruby-gems/idlc/lib/idlc/idl.treetop index 63fb53843..83fd0b77f 100644 --- a/tools/ruby-gems/idlc/lib/idlc/idl.treetop +++ b/tools/ruby-gems/idlc/lib/idlc/idl.treetop @@ -331,11 +331,9 @@ grammar Idl rule bits_cast '$bits' space* '(' space* expr:( - csr_register_access_expression - / - enum_ref - / expression + / + csr_register_access_expression ) space* ')' end