-
Notifications
You must be signed in to change notification settings - Fork 168
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
Implementing callbacks in sail-riscv for state-changing events #494
base: master
Are you sure you want to change the base?
Implementing callbacks in sail-riscv for state-changing events #494
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks along the lines I was thinking, though I think we will still need to have RVFI support in the C emulator, so you still need the flags. It will just need to be implemented via the callbacks.
I probably wouldn't use __attribute__((weak))
. If anyone is modifying the callbacks they'll have their own fork of the emulator anyway and can just delete the default ones. That's what we (Codasip) will do.
I'll get you some code to read lbits
.
It's something like this in C++ (adapted from our code, so this exact code isn't tested).
In crappy C you will have to deal with |
model/riscv_insts_vext_mem.sail
Outdated
@@ -154,6 +154,7 @@ function process_vlsegff (nf, vm, vd, load_width_bytes, rs1, EMUL_pow, num_elem) | |||
if i == 0 then { ext_handle_data_check_error(e); return RETIRE_FAIL } | |||
else { | |||
vl = to_bits(sizeof(xlen), i); | |||
if rv_enable_callbacks then csr_update_callback("vl", vl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having to add these manually is rather error prone and makes the code less readable. Can we do some fancy sail overloading tricks to automatically call these functions for CSR writes (e.g. something like CSR(vl) = ...
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Various CSRs have a set_foo()
function. We could put it in there. Though that doesn't significantly reduce the risk because people can always forget to use the setter.
I ran into a similar issue recently - trying to memoize/cache a value. Difficult to guarantee that all the code will use setter that updates the cache without some support for private
.
IMO there are few enough of these that we can probably ignore this issue for the moment, though I agree it's not ideal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it would be better to get Sail to generate the callbacks automatically?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we discussed this before. Might be nice, but I'm not 100% sure it would do exactly what you want, e.g. you probably want distinct callbacks for writes to sstatus
and mstatus
, but under the hood they write to the same register
. Also there are very few places this callback is needed so the manual burden is not that high, and I suspect Alasdair has enough on his plate already!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the ext_notification_write_CSR
(and ext_notification_read_CSR
) function. It helps to simplify writing to a CSR with a call to a callback.
Actually reading this back, I wonder if |
I would like to point out that the callback functions are annotated as |
I would say if someone edits the code and changes the callback to be impure then it's up to them to also change the Sail code to mark them as impure. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you keep the RVFI-DII code around? Alternatively, a good demonstrator of the callbacks would be to implement the RVFI-DII socket interface using those callbacks?
Yeah to be clear I don't think these callbacks should be user defined. We should have hard-coded callbacks that support RVFI, logging, etc. |
10ae49b
to
6e0f187
Compare
@arichardson, @Timmmm, what do you think of the current version? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this is pretty much exactly what I was thinking. I put some minor comments but I it looks like the right direction to me.
c_emulator/riscv_default_callbacks.c
Outdated
} | ||
int xreg_update_callback(unsigned reg, uint64_t value) { } | ||
int freg_update_callback(unsigned reg, uint64_t value) { } | ||
int csr_update_callback(const char *reg_name, uint64_t value) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to use the CSR index than the name. If this function wants to access the name it can call zcsr_name_map_forwards()
or whatever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, now here's the argument is unsigned reg
, and in the sail -- csreg
.
model/riscv_insts_vext_mem.sail
Outdated
@@ -153,25 +153,22 @@ function process_vlsegff (nf, vm, vd, load_width_bytes, rs1, EMUL_pow, num_elem) | |||
Ext_DataAddr_Error(e) => { | |||
if i == 0 then { ext_handle_data_check_error(e); return RETIRE_FAIL } | |||
else { | |||
vl = to_bits(sizeof(xlen), i); | |||
print_reg("CSR vl <- " ^ BitStr(vl)); | |||
ext_notification_write_CSR(csr_name_map("vl"), to_bits(sizeof(xlen), i)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not 100% sure about this change. For now I would probably just replace the print_reg
s with calls to csr_update_callback
. We can do this in a future PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Deleted ext_notification_write_CSR
and replaced print_reg
s with calls to csr_update_callback
.
ocaml_emulator/platform.ml
Outdated
let pc_update_callback value = () | ||
let xreg_update_callback (reg, value) = () | ||
let freg_update_callback (reg, value) = () | ||
let csr_update_callback (reg_name, value) = () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor naming but I'd replace update
with write
here. Everywhere else talks about writing registers and memory, not "updating" them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
model/riscv_types.sail
Outdated
$ifdef RVFI_DII | ||
register rv_enable_callbacks : bool = true | ||
$else | ||
register rv_enable_callbacks : bool = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can probably omit this and call them unconditionally. I doubt it will make any noticeable difference to performance and it will simplify the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I call them here unconditionally and check rv_enable_callbacks
in the callback functions.
@@ -340,8 +340,7 @@ function trap_handler(del_priv : Privilege, intr : bool, c : exc_code, pc : xlen | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a load of implicit CSR writes here that need to be tracked. We do it like this:
function track_trap(p : Privilege, intr : bool, c : exc_code) -> unit = {
track_writeCSR(csr_name_map("mstatus"));
match (p) {
Machine => {
track_writeCSR(csr_name_map("mcause"));
track_writeCSR(csr_name_map("mtval"));
track_writeCSR(csr_name_map("mepc"));
},
Supervisor => {
track_writeCSR(csr_name_map("scause"));
track_writeCSR(csr_name_map("stval"));
track_writeCSR(csr_name_map("sepc"));
},
User => {
track_writeCSR(csr_name_map("ucause"));
track_writeCSR(csr_name_map("utval"));
track_writeCSR(csr_name_map("uepc"));
},
};
}
model/riscv_mem.sail
Outdated
if rv_enable_callbacks then | ||
match result { | ||
MemValue(_) => mem_update_callback(paddr, width, value, /* is_exception */ false), | ||
MemException(_) => mem_update_callback(paddr, width, value, /* is_exception */ true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if it's necessarily better, but we only call out mem_read_callback()
function on success MemValue()
, and then we have another callback for traps in trap_handler()
, where the print_reg()
s are.
Maybe keep this (because you can easily ignore the exception case) but also add the trap callback.
You might want to add the exception code to the callback to make logging a bit easier:
match result {
MemValue(_) => mem_write_callback(paddr, width, value),
MemException(e) => mem_exception_callback(paddr, width, num_of_ExceptionType(e)),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, added mem_exception_callback
. But the only thing is, I call it without width
since num_of_ExceptionType
has type {'n, (0 <= 'n < xlen). int('n)}
and width
has type {'n, (0 < 'n <= max_mem_access). int('n)}
. To simplify the function declaration in the sail. Especially in case of write failure only the memory address is logged and the width
is not needed.
model/riscv_fdext_regs.sail
Outdated
@@ -157,6 +157,7 @@ function rF r = { | |||
val wF : forall 'n, 0 <= 'n < 32. (regno('n), flenbits) -> unit | |||
function wF (r, in_v) = { | |||
assert(sys_enable_fdext()); | |||
if rv_enable_callbacks then freg_update_callback(regno_to_regidx(r), in_v); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO a better design is to push the enable check down from the caller rather than replicate it everywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, put rv_enable_callback
into the riscv_sim.c
and checking it in the callbacks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall I think this approach looks good, but I'd push all the rv_enable_callbacks into the callee to simplify the code. Since the variable is not a compile-time constant it shouldn't matter whether it's checked in the C code or sail code. It might be easier to keep this as an internal flag to the C/Ocaml emulators.
09acccc
to
be696b8
Compare
/* TODO: will only print bits; should we print in floating point format? */ | ||
print_reg("f" ^ dec_str(r) ^ " <- " ^ FRegStr(v)); | ||
|
||
freg_write_callback(regno_to_regidx(r), in_v); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default callback needs to print the register otherwise this removes tracing capabilities from the model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added an implementation of the callbacks for trace printing to the riscv_types.sail
, riscv_csr_begin.sail
, riscv_vreg_type.sail
and call them from the default callbacks in the riscv_default_callbacks.c
.
98f3973
to
7dcbd56
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not reviewed this in detail, but overall the new approach looks sensible to me.
model/riscv_types.sail
Outdated
val mem_write_callback_default : forall 'n, 0 < 'n <= max_mem_access . (/* addr */ xlenbits, /* width */ int('n), /* value */ bits(8 * 'n)) -> unit | ||
function mem_write_callback_default (addr, width, value) = { | ||
print_mem("mem[" ^ BitStr(addr) ^ "] <- " ^ BitStr(value)) | ||
} | ||
|
||
val mem_read_callback_default : forall 'n, 0 < 'n <= max_mem_access . (/* access type */ string, /* addr */ xlenbits, /* width */ int('n), /* value */ bits(8 * 'n)) -> unit | ||
function mem_read_callback_default (t, addr, width, value) = { | ||
print_mem("mem[" ^ t ^ "," ^ BitStr(addr) ^ "] -> " ^ BitStr(value)) | ||
} | ||
|
||
val xreg_write_callback_default : (regidx, xlenbits) -> unit | ||
function xreg_write_callback_default (reg, value) = { | ||
print_reg("x" ^ dec_str(regidx_to_regno(reg)) ^ " <- " ^ BitStr(value)) | ||
} | ||
|
||
val freg_write_callback_default : (regidx, flenbits) -> unit | ||
function freg_write_callback_default (reg, value) = { | ||
/* todo: will only print bits; should we print in floating point format? */ | ||
print_reg("f" ^ dec_str(regidx_to_regno(reg)) ^ " <- " ^ BitStr(value)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that the OCaml simulator is gone, I wonder if we should just move these default callbacks to C? Doing the sail -> C -> sail roundtrip seems a bit unnecessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, it's done.
acbdacc
to
779fbf7
Compare
1. Added callback functions for XRegs, FRegs, VRegs, PC, CSR, memory writes and CSR, memory reads. 2. Added implementation of callbacks for RVFI records in riscv_rvfi_callbacks.c. 3. Added default callback OCaml-implementations in the file platform.ml.
…v_default_callbacks.c
779fbf7
to
232e5bd
Compare
@arichardson, @Timmmm, what do you think of the current version? |
I have an initial implementation for the suggestion described in #449 . However I still have some questions related to the implementation details and would like to get some advise (if possible).