Skip to content

Big endianness Support added #751

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 30 commits into
base: master
Choose a base branch
from

Conversation

MuhammadHammad001
Copy link
Contributor

@MuhammadHammad001 MuhammadHammad001 commented Feb 19, 2025

Hi @jordancarlin and @Timmmm
This PR adds the support for the big endianness in Sail RISC-V.

The test for the verification of this support is also added in this PR.

Copy link
Collaborator

@jordancarlin jordancarlin left a comment

Choose a reason for hiding this comment

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

Thanks for working on this! I left some initial comments below.

One other thing that I don't see here is the necessary modification to the reset function. Reset should set endianness back to little-endian if supported.

@@ -236,3 +236,6 @@ type max_mem_access : Int = 4096

// Type used for memory access widths. Zero byte accesses are not allowed.
type mem_access_width = range(1, max_mem_access)

// Function to reverse endianness.
val reverse_endianness = pure {c: " reverse_endianness"} : forall 'n . (bits('n * 8)) -> bits('n * 8)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any reason not to just use the existing sail reverse_endianness library? https://github.com/rems-project/sail/blob/6a8d444e41a3e04798e874325bf4ec0f813e3a3a/lib/reverse_endianness.sail#L47

Just put a $include <reverse_endianness.sail> at the top of the prelude.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I actually tried this but was getting the following error:
image

This is because of the difference of types being passed by the function (bits(8 * 'n)) and type taken by the function bits('n).
Actually, this solution was suggested by @Timmmm and this method made the function accept values forall 'n instead of using contrained values.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@Alasdair I know you're planning on a new version of Sail soon. Any chance the reverse_endianness library could be updated so that it can be used here?

Copy link

github-actions bot commented Feb 19, 2025

Test Results

402 tests  +2   402 ✅ +2   1m 45s ⏱️ +2s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 33752f0. ± Comparison against base commit e0cf0c8.

♻️ This comment has been updated with latest results.

@nadime15
Copy link
Contributor

nadime15 commented Feb 19, 2025

I just noticed that sstatus also has a UBE bit. It should be added to bitfield Sstatus including lower_mstatus() and lift_sstatus()

@jordancarlin jordancarlin linked an issue Feb 20, 2025 that may be closed by this pull request
@pmundkur
Copy link
Collaborator

The readme can also be updated to mention this support.

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.

Looking pretty close to me. I think we will need some kind of testing for this... It feels like the sort of thing that is easy to screw up without knowing. How do you compile a big endian ELF though?

@Timmmm
Copy link
Collaborator

Timmmm commented Feb 26, 2025

Ah never mind, I saw your test.

@nadime15 nadime15 mentioned this pull request Feb 26, 2025
2 tasks
@jordancarlin
Copy link
Collaborator

@MuhammadHammad001 Seems like this is pretty close. Would be good to get merged before conflicts start to build. Any chance you can finish it off soon?

@Timmmm
Copy link
Collaborator

Timmmm commented Mar 7, 2025

The test for the verification of this support is available here.

I just merged support for first-party tests so we could add this as part of this PR now!

@MuhammadHammad001
Copy link
Contributor Author

@MuhammadHammad001 Seems like this is pretty close. Would be good to get merged before conflicts start to build. Any chance you can finish it off soon?

Hi Jordan and Tim, sorry for the late response. I will commit the requested changes today.

Copy link
Collaborator

@jordancarlin jordancarlin left a comment

Choose a reason for hiding this comment

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

Looks very close at this point! One more small comment.

@MuhammadHammad001
Copy link
Contributor Author

The test for the verification of this support is available here.

I just merged support for first-party tests so we could add this as part of this PR now!

Okayy, I will add this in the next commit.

Copy link
Collaborator

@jordancarlin jordancarlin left a comment

Choose a reason for hiding this comment

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

LGTM once the test is added. Down the road we should add a configuration option to enable or disable big endianness support. Feel free to add a TODO comment in the code where the value of MBE is set to remind us of this.

@jordancarlin jordancarlin requested a review from Timmmm March 13, 2025 17:56
@@ -63,8 +63,16 @@ function write_kind_of_flags (aq : bool, rl : bool, con : bool) -> write_kind =
(true, false, true) => throw(Error_not_implemented("sc.aq"))
}

function is_big_endian(typ : AccessType(ext_access_type)) -> bool = {
Copy link
Collaborator

@jordancarlin jordancarlin Apr 9, 2025

Choose a reason for hiding this comment

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

Why not have this be something like get_endianness that returns a type from the Endianness enum and push the check for an Execute access into it? So it becomes something like:

function get_endianness(typ : AccessType(ext_access_type)) -> Endianness = {
     match (typ, effectivePrivilege(typ, mstatus, cur_privilege)) {
       (Execute(), _)   => LittleEndian
       (_, Machine)     => if bits_to_bool(mstatus[MBE]) then BigEndian else LittleEndian,
       (_, Supervisor)  => if bits_to_bool(mstatus[SBE]) then BigEndian else LittleEndian,
       (_, User)        => if bits_to_bool(mstatus[UBE]) then BigEndian else LittleEndian,
     }
 }

Then you can replace all of the uses of let current_endianness: Endianness = if is_big_endian(typ) & typ != Execute() then BigEndian else LittleEndian with just get_endianness(typ).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea! I will implement this

Signed-off-by: Muhammad Hammad Bashir <[email protected]>
@Timmmm
Copy link
Collaborator

Timmmm commented Apr 9, 2025

I wonder if we should try to get #467 merged before this, since I don't think they will work together currently, and I think the endianness conversion should actually go in vmem_read/write().

@MuhammadHammad001
Copy link
Contributor Author

Hi @Timmmm Any update on merging this PR? Is there anybody working on the PR #467 ?

Signed-off-by: Muhammad Hammad Bashir <[email protected]>
@jordancarlin
Copy link
Collaborator

Any update on merging this PR? Is there anybody working on the PR #467 ?

@Alasdair and @pmundkur are working on #467 and it should hopefully be updated soon.

@pmundkur
Copy link
Collaborator

Hi @Timmmm Any update on merging this PR? Is there anybody working on the PR #467 ?

I'm working on rebasing this PR. I should have something pretty soon.

@jordancarlin
Copy link
Collaborator

@MuhammadHammad001 Thanks for your patience with this one. #861 (which replaced #467) is merged, so it should be possible to update this PR now to move the endianness conversion into the new functions that it introduced.

@MuhammadHammad001
Copy link
Contributor Author

@MuhammadHammad001 Thanks for your patience with this one. #861 (which replaced #467) is merged, so it should be possible to update this PR now to move the endianness conversion into the new functions that it introduced.

Sure, I will update this accordingly.

@MuhammadHammad001
Copy link
Contributor Author

Hi, @jordancarlin @pmundkur @Timmmm , can you please review this and let me know if I need to change anything?

Also, I have removed the testcase for the mtime since the value is changed before the check causing it to fail. But, mtimecmp and htif testcases are present to make sure the endinanness is supported for the MMIOs.

Copy link
Collaborator

@jordancarlin jordancarlin left a comment

Choose a reason for hiding this comment

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

Maybe I'm missing something, but I though the whole point of waiting for #861 was to move the endianness handling into vmem_read/vmem_write so that it didn't need to be duplicated everywhere. Right now it looks like it's still being handled in mem_write_value_priv_meta and the individual mmio functions.

@MuhammadHammad001
Copy link
Contributor Author

Maybe I'm missing something, but I though the whole point of waiting for #861 was to move the endianness handling into vmem_read/vmem_write so that it didn't need to be duplicated everywhere. Right now it looks like it's still being handled in mem_write_value_priv_meta and the individual mmio functions.

I may be mis-understanding but as I mentioned earlier, I can surely do this for vmem_write but for vmem_read, I am unable to understand, how this will be possible because in case of a read, we print the data where we read that data. For instance, in case of a mmio read, the following part of the clint_load code:

val clint_load : forall 'n, 'n > 0. (AccessType(ext_access_type), physaddr, int('n), Endianness) -> MemoryOpResult(bits(8 * 'n))
function clint_load(t, Physaddr(addr), width, current_endianness) = {
  //these two registers are 64 bits, so simple conversion is not accurate.
  //consider 0x78563412 stored at mtime/cmp and when reading in big endian format,
  //we change it to 0x1234567800000000. So position for mtime and mtime_hi are reversed. (not for ld)
  var converted_mtime    = endianness_conversion(mtime, current_endianness);
  var converted_mtimecmp = endianness_conversion(mtimecmp, current_endianness);
  if ((current_endianness == BigEndian) & 'n == 4) then {
                converted_mtime    = converted_mtime[31..0] @ converted_mtime[63..32];
                converted_mtimecmp = converted_mtimecmp[31..0] @ converted_mtimecmp[63..32];
                };
  let addr = addr - plat_clint_base ();
  /* FIXME: For now, only allow exact aligned access. */
  if addr == MSIP_BASE & ('n == 8 | 'n == 4)
  then {
    if   get_config_print_platform()
    then print_platform("clint[" ^ BitStr(addr) ^ "] -> " ^ BitStr(mip[MSI]));
    Ok(zero_extend(sizeof(8 * 'n), mip[MSI]))
  }
  else if addr == MTIMECMP_BASE & ('n == 4)
  then {
    if   get_config_print_platform()
    then print_platform("clint<4>[" ^ BitStr(addr) ^ "] -> " ^ BitStr(converted_mtimecmp[31..0]));
    /* FIXME: Redundant zero_extend currently required by Lem backend */
    Ok(zero_extend(32, converted_mtimecmp[31..0]))

As you can see, the

then print_platform("clint<4>[" ^ BitStr(addr) ^ "] -> " ^ BitStr(converted_mtimecmp[31..0]));

is printing the data here instead in the vmem_read function. Now, even if the vmem_read function gets the data reversed, in the log file, we will see the actual (non-reversed) data.

Note: I am referring reversed == Big endian

Can you please help me in clearing the doubt?

@pmundkur
Copy link
Collaborator

I'm not sure what to do about printing: it would be confusing to see endianness-adjusted writes but unadjusted reads in the logs. Also, vmem_{read,write} currently don't cover AMO instructions, so endianness control will have to be added there as well.

@MuhammadHammad001
Copy link
Contributor Author

But, I am converting the data to be written inside the vmem_write function, so even if it's called seperately for amo instructions, this will cover that as well. Am I right?

@pmundkur
Copy link
Collaborator

But, I am converting the data to be written inside the vmem_write function, so even if it's called seperately for amo instructions, this will cover that as well. Am I right?

AMO instructions are not using vmem_{read,write} functions at all. So any changes in those functions will not be seen by AMOs.

@MuhammadHammad001
Copy link
Contributor Author

Sorry @pmundkur, I may be mis-understanding, I am currently looking at the file riscv_instr_aext.sail and here, it is using the function vmem_write
image

Can you please give a pointer to exact function you want me to change?

@pmundkur
Copy link
Collaborator

Can you please give a pointer to exact function you want me to change?

There is a mem_read and mem_write_value in execute(AMO(...)) in that file that will need handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for mstatus MBE, SBE
6 participants