Skip to content

Add Zilsd/Zclsd Support#765

Open
trdthg wants to merge 5 commits intoriscv:masterfrom
trdthg:zilsd
Open

Add Zilsd/Zclsd Support#765
trdthg wants to merge 5 commits intoriscv:masterfrom
trdthg:zilsd

Conversation

@trdthg
Copy link
Collaborator

@trdthg trdthg commented Mar 4, 2025

spec: https://github.com/riscv/riscv-zilsd/blob/main/zilsd.adoc#insns-sd

function clause extensionEnabled(Ext_Zclsd) = true & xxx is a placeholder, waiting for config system

@github-actions
Copy link

github-actions bot commented Mar 4, 2025

Test Results

2 314 tests  ±0   2 314 ✅ ±0   32m 0s ⏱️ -6s
    1 suites ±0       0 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit ba2adbe. ± Comparison against base commit 9397607.

♻️ This comment has been updated with latest results.

Comment on lines +27 to +45
match ext_data_get_addr(rs1, offset, Read(Data), width_bytes) {
Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL },
Ext_DataAddr_OK(vaddr) =>
if check_misaligned(vaddr, width)
then { handle_mem_exception(vaddr, E_Load_Addr_Align()); RETIRE_FAIL }
else match translateAddr(vaddr, Read(Data)) {
TR_Failure(e, _) => { handle_mem_exception(vaddr, e); RETIRE_FAIL },
TR_Address(paddr, _) =>
match mem_read(Read(Data), paddr, 8, false, false, false) {
Ok(result) => {
X(rd) = result[31..0];
X(rd + 1) = result[63..32];
RETIRE_SUCCESS
},
Err(e) => { handle_mem_exception(vaddr, e); RETIRE_FAIL },
},
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious, wouldn't it make more sense to replace LOAD_RV32 (and STORE_RV32) with two sequential 32-bit load/store operations instead of one 64-bit load/store operation?

Most 32-bit systems have a 32-bit memory bus, so performing two 32-bit accesses would be more realistic than a single 64-bit access on such hardware.

This implementation would also allow us to test the more interesting case where interrupts could occur between the two operations, as explicitly permitted by the Zilsd specification.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's definitely a good idea, I didn't think of that

@trdthg trdthg force-pushed the zilsd branch 2 times, most recently from a979234 to a28bd1b Compare March 5, 2025 01:02
@jordancarlin jordancarlin added the extension Adds support for a RISC-V extension label Mar 5, 2025
function clause extensionEnabled(Ext_Zilsd) = true & xlen == 32

/* ****************************************************************** */
union clause ast = LOAD_RV32 : (bits(12), regidx, regidx)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOAD_DOUBLE might be a better name for the AST constructor than LOAD_RV32. It's unfortunate that the spec re-uses LOAD. (Could we ask the spec authors to change this before ratification?) Similarly for STORE_RV32.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw your reply at riscvarchive/riscv-zilsd#64, so I update it to the format of EXT_INST(ZILSD_LD...).

@christian-herber-nxp
Copy link

how does this relate to #490?

@trdthg
Copy link
Collaborator Author

trdthg commented Mar 10, 2025

how does this relate to #490?

I didn't see it before. Currently, the code mainly has the following differences

@trdthg trdthg force-pushed the zilsd branch 2 times, most recently from d6533f5 to d70a272 Compare March 10, 2025 01:30
@pmundkur pmundkur added the tgmm-agenda Tagged for the next Golden Model meeting agenda. label Mar 10, 2025
@pmundkur
Copy link
Collaborator

@trdthg Could you check with @christian-herber-nxp to make sure we're not missing anything from #490 and we get the appropriate co-authors?

@christian-herber-nxp
Copy link

the author of the original PR should be clear from that, it is @simonacostinescu

@trdthg trdthg force-pushed the zilsd branch 2 times, most recently from c1bff11 to ea18ab9 Compare March 24, 2025 02:43
@trdthg
Copy link
Collaborator Author

trdthg commented Mar 24, 2025

Update

I am trying to run act tests (there is already a PR, not merged yet), but encountered a few problems

I think you could review this after I finished those tests.

Co-authored-by already added

@jordancarlin jordancarlin removed the tgmm-agenda Tagged for the next Golden Model meeting agenda. label Apr 7, 2025
@trdthg trdthg force-pushed the zilsd branch 2 times, most recently from b7f8bbe to 5864d1a Compare February 4, 2026 03:33
@trdthg
Copy link
Collaborator Author

trdthg commented Feb 4, 2026

Rebased. I also added two first-party tests. Since I need to specify specific config for these two tests, I changed the CMake a lot and introduced two python scripts:

  • test/first_party/scripts/test_matrix.py: Used to define configs for different tests, where you can specify -match, xlen, and an override parameter to indicate which fields in the default JSON should be overridden. CMake is not good at defining such data structure.
  • test/first_party/scripts/merge_config.py: Used to merge override_json and default_json. because jq dosn't support our comments in json.

Btw, I don't really understand how to properly write $[wavedrom], such as how each segment maps to sail and the meaning of various _ symbols. For now, I just copied the attr fields directly from the manual:

{reg: [
  {bits: 2, name: 0x2,      type: 8, attr: ['C2']},
  {bits: 5, name: 'rs2',    type: 4, attr: ['src, src[0]=0']},
  {bits: 6, name: 'imm',    type: 3, attr: ['offset[5:3|8:6]']},
  {bits: 3, name: 0x7,      type: 8, attr: ['C.SDSP']},
], config: {bits: 16}}

Copy link
Collaborator

@Timmmm Timmmm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been really really really trying to avoid adding any Python, since it usually ends up being a complete nightmare. It's not quite so bad these days with uv but if we do add any we will have to do the whole pyproject.toml, Pyright, Pylint stuff.

My strong preference would be to use almost anything other than Python. Maybe Deno.

function clause execute (ZCLSD_C_LD(uimm, rsc, rdc)) = {
let rd = creg2reg_idx(rdc);
let rs = creg2reg_idx(rsc);
execute(ZILSD_LD(zero_extend(uimm), rs, rd))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use ExecuteAs.

function clause execute ZILSD_SD(imm, rs2, rs1) = {
let base_addr = X(rs1);
let rs2_val = X(rs2);
let rs2_pair_val = if rs2 != zreg then X(rs2+1) else rs2_val;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have X_pair for this.

Some(e) => Memory_Exception(vaddr, e),
None() => {
let vaddr = Virtaddr(base_addr + sign_extend(imm + 4));
match load_imm(vaddr, 4, rd+1) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. It will write to x1 if you load into x0. I'd use X_pair here too.

$[wavedrom "C.LDSP offset[5] dest offset[4:3|8:6] C2"]
mapping clause encdec_compressed = ZCLSD_C_LDSP(ui86 @ ui5 @ ui43 @ 0b000, rd)
<-> 0b011 @ ui5 : bits(1) @ encdec_reg(rd) @ ui43 : bits(2) @ ui86 : bits(3) @ 0b10
when currentlyEnabled(Ext_Zclsd)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For C.LDSP, usage of x0 as the destination is reserved.

$[wavedrom "C.SDSP offset[5:3|8:6] src C2"]
mapping clause encdec_compressed = ZCLSD_C_SDSP(ui86 @ ui53 @ 0b000, rs2)
<-> 0b111 @ ui53 : bits(3) @ ui86 : bits(3) @ encdec_reg(rs2) @ 0b10
when currentlyEnabled(Ext_Zclsd)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These compressed encodings are missing the odd register check I think?

Also double check how we do it for the other instructions that only allow even registers (Zfinx). We have a config setting to configure that behaviour so we should make the code the same style at least.

"supported": true
},
"Zilsd": {
"supported": true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is only supported on RV32 so this should be

Suggested change
"supported": true
"supported": @CONFIG_XLEN_IS_32@

Also we should probably add a check to validate_config.sail to make sure you don't set this on RV64.

@arichardson
Copy link
Collaborator

I've been really really really trying to avoid adding any Python, since it usually ends up being a complete nightmare. It's not quite so bad these days with uv but if we do add any we will have to do the whole pyproject.toml, Pyright, Pylint stuff.

My strong preference would be to use almost anything other than Python. Maybe Deno.

I think python is fine as long as it is limited to 3.8 (or whatever the version in RHEL8 is) with only the standard library. Otherwise I agree that it can be a dependency hell.

@@ -0,0 +1,38 @@
#!/usr/bin/env python3
import json
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This script shouldn't be too difficult to write in CMake?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@trdthg trdthg force-pushed the zilsd branch 5 times, most recently from 542c5b7 to a5a5afe Compare February 5, 2026 08:46
@Timmmm
Copy link
Collaborator

Timmmm commented Feb 5, 2026

I haven't used any third-party libraries in the scripts, so it‘s not necessary to introduce uv or any other management tools.

Yeah but third party libraries always creep in so even in that case you need to start with a proper setup. If we do start using Python we're going to end up using at least Pydantic and Typer.

As for Deno, I feel that anything requiring extra installation won't be easily accepted.

Deno is easier to install than Python + uv. It's literally one command. I don't think the avoidance of a single command is worth the pain of Python.

What if we just rewrite those two scripts in cpp?

I think that's a good idea.

@Timmmm Timmmm added the tgmm-agenda Tagged for the next Golden Model meeting agenda. label Feb 5, 2026
@trdthg
Copy link
Collaborator Author

trdthg commented Feb 5, 2026

Lean is not happy again. But this is a bit tricky because Zcf and Zclsd seem to be at the same level.

It(Zclsd) has overlapping encodings with Zcf and is thus incompatible with Zcf.

The error msg is

  error: LeanRV64D/PlatformConfig.lean:5680:20: failed to prove termination, possible solutions:
    - Use `have`-expressions to prove the remaining goals
    - Use `termination_by` to specify a different well-founded relation
    - Use `decreasing_by` to specify your own tactic for discharging this kind of goal
  ⊢ max (currentlyEnabled_measure Ext_Zclsd) 0 < currentlyEnabled_measure Ext_Zcf
  error: Lean exited with code 1
  Some required targets logged failures:
  - LeanRV64D.PlatformConfig

but if you write Zcf > Zclsd

    Ext_Zcf => 3, // > C
    Ext_Zclsd => 2, // > C

It will become:

⊢ max (currentlyEnabled_measure Ext_Zcf) 0 < currentlyEnabled_measure Ext_Zclsd

Copy link
Collaborator

@Timmmm Timmmm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better! The --config-override thing seems to be a lot simpler and nicer than I would have guessed. I think it needs to go in its own PR though.

@trdthg trdthg force-pushed the zilsd branch 2 times, most recently from ead8f1a to 79deb26 Compare March 6, 2026 02:53
@pmundkur pmundkur removed the tgmm-agenda Tagged for the next Golden Model meeting agenda. label Mar 7, 2026
Copy link
Collaborator

@pmundkur pmundkur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty close; just minor fixes.

Co-authored-by:	Simona Costinescu <simona.costinescu@nxp.com>
@pmundkur
Copy link
Collaborator

@trdthg i made some minor fixes, hope that's okay. I wonder what you think about the TODO I left.

@trdthg
Copy link
Collaborator Author

trdthg commented Mar 17, 2026

LGTM. For the alignment issue, my current thought is to either modify the function definitions directly or overload [vmem_read_addr, vmem_write_addr, vmem_read, vmem_write], let them accept an align_width parameter. For example:

diff --git a/model/extensions/Zilsd/zilsd_insts.sail b/model/extensions/Zilsd/zilsd_insts.sail
index 60c7fd7b..4ea5f8a2 100644
--- a/model/extensions/Zilsd/zilsd_insts.sail
+++ b/model/extensions/Zilsd/zilsd_insts.sail
@@ -29,9 +29,10 @@ mapping clause encdec = ZILSD_LD(imm, rs1, rd)
 function clause execute ZILSD_LD(imm, rs1, rd) = {
   assert(xlen == 32);
   let access = Load(Data);
-  match vmem_read(rs1, sign_extend(imm), 4, access, false, false, false) {
+  let align_width = if true then 8 else 4;
+  match vmem_read(rs1, sign_extend(imm), 4, align_width, access, false, false, false) {
     Ok(lo) => {
-      match vmem_read(rs1, sign_extend(imm + 4), 4, access, false, false, false) {
+      match vmem_read(rs1, sign_extend(imm + 4), 4, align_width, access, false, false, false) {
         Ok(hi) => { X_pair(rd) = hi @ lo; RETIRE_SUCCESS },
         Err(e) => e,
       }
diff --git a/model/sys/vmem_utils.sail b/model/sys/vmem_utils.sail
index a54d44f2..4dde6a74 100644
--- a/model/sys/vmem_utils.sail
+++ b/model/sys/vmem_utils.sail
@@ -130,14 +130,14 @@ private function access_causes_misaligned_exception(
 
 // External API
 
-val vmem_read_addr : forall 'width, is_mem_width('width).
-  (virtaddr, xlenbits, int('width), MemoryAccessType(mem_payload), bool, bool, bool)
+val vmem_read_addr_with_custom_align : forall 'width 'align_width, is_mem_width('width) & is_mem_width('align_width).
+  (virtaddr, xlenbits, int('width), int('align_width), MemoryAccessType(mem_payload), bool, bool, bool)
   -> result(bits(8 * 'width), ExecutionResult)
 
-function vmem_read_addr(vaddr, offset, width, access, aq, rl, res) = {
+function vmem_read_addr_with_custom_align(vaddr, offset, width, align_width, access, aq, rl, res) = {
   // "LR faults like a normal load, even though it's in the AMO major opcode space."
   // - Andrew Waterman, isa-dev, 10 Jul 2018.
-  if access_causes_misaligned_exception(vaddr, width, res)
+  if access_causes_misaligned_exception(vaddr, align_width, res)
   then return Err(Memory_Exception(vaddr, E_Load_Addr_Align()));
 
   // If the load is misaligned or an allowed misaligned access, split into `n`
@@ -180,6 +180,16 @@ function vmem_read_addr(vaddr, offset, width, access, aq, rl, res) = {
   Ok(data)
 }
 
+val vmem_read_addr_with_width_align : forall 'width, is_mem_width('width).
+  (virtaddr, xlenbits, int('width), MemoryAccessType(mem_payload), bool, bool, bool)
+  -> result(bits(8 * 'width), ExecutionResult)
+
+function vmem_read_addr_with_width_align(vaddr, offset, width, access, aq, rl, res) = {
+  vmem_read_addr_with_custom_align(vaddr, offset, width, width, access, aq, rl, res)
+}
+
+overload vmem_read_addr = { vmem_read_addr_with_width_align, vmem_read_addr_with_custom_align }
+
 // This lets the Rocq extraction know why the repeat loop above terminates
 termination_measure vmem_read_addr repeat n
 
@@ -252,11 +262,11 @@ function vmem_write_addr(vaddr, width, data, access, aq, rl, res) = {
 // This lets the Rocq extraction know why the repeat loop above terminates
 termination_measure vmem_write_addr repeat n
 
-val vmem_read : forall 'width, is_mem_width('width).
-  (regidx, xlenbits, int('width), MemoryAccessType(mem_payload), bool, bool, bool)
+val vmem_read_with_custom_align : forall 'width 'align_width, is_mem_width('width) & is_mem_width('align_width).
+  (regidx, xlenbits, int('width), int('align_width), MemoryAccessType(mem_payload), bool, bool, bool)
   -> result(bits(8 * 'width), ExecutionResult)
 
-function vmem_read(rs, offset, width, access, aq, rl, res) = {
+function vmem_read_with_custom_align(rs, offset, width, align_width, access, aq, rl, res) = {
   // Get the address, X(rs1) + offset.
   // Extensions might perform additional checks on address validity.
   let vaddr : virtaddr = match ext_data_get_addr(rs, offset, access, width) {
@@ -264,9 +274,19 @@ function vmem_read(rs, offset, width, access, aq, rl, res) = {
     Ext_DataAddr_Error(e)  => return Err(Ext_DataAddr_Check_Failure(e)),
   };
 
-  vmem_read_addr(vaddr, offset, width, access, aq, rl, res)
+  vmem_read_addr_with_custom_align(vaddr, offset, width, align_width, access, aq, rl, res)
 }
 
+val vmem_read_with_width_align : forall 'width, is_mem_width('width).
+  (regidx, xlenbits, int('width), MemoryAccessType(mem_payload), bool, bool, bool)
+  -> result(bits(8 * 'width), ExecutionResult)
+
+function vmem_read_with_width_align(rs, offset, width, access, aq, rl, res) = {
+  vmem_read_with_custom_align(rs, offset, width, width, access, aq, rl, res)
+}
+
+overload vmem_read = { vmem_read_with_custom_align, vmem_read_with_width_align }
+
 val vmem_write : forall 'width, is_mem_width('width).
   (regidx, xlenbits, int('width), bits(8 * 'width), MemoryAccessType(mem_payload), bool, bool, bool)
   -> result(bool, ExecutionResult)

Or we just add an extra alignment check for Zilsd? This would be the simplest approach. I personally prefer this option. :D

@pmundkur
Copy link
Collaborator

I too prefer the simpler approach of an extra alignment check in Zilsd, and making it depend on a configuration option (something like require_8byte_aligned_access).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extension Adds support for a RISC-V extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants