-
Notifications
You must be signed in to change notification settings - Fork 155
added $random, $urandom_range and better $value$plusargs #780
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
base: main
Are you sure you want to change the base?
Conversation
…, and handling pointer to value read
…width, though upto 64 is supported
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.
Thank you for contributing! It would be great add support for $value$plusargs
! Note that Issue #302 is open for this, and includes some discussion of some of the design decisions and how it might be implemented.
I would suggest separating this into at least two PRs, one for the random tasks and one for plusargs, so that one doesn't hold the other up from being accepted.
I did not remember the support for $random
, so I had to look up the status. It appears that $random
is supported in the Verilog generation, but there are no tests in the testsuite and it is not mentioned in the language guides or the library guide. (That's unfortunate, because we should have tests and it should be documented.) The one documentation mention is in the user guide, which says that Bluesim and Verilog will give different results -- but actually Bluesim doesn't support $random
! Bluesim nearly supports it, but is just missing a definition of dollar_random
in its codebase.
So one thing you've done is to add dollar_random
, which is great! However, it should not be added to the unrelated file dollar_time.cxx
. Instead, a new file dollar_random.cxx
should be created, in which any random-related tasks can be placed.
A question I have, though, is whether the Bluesim and Verilog implementations need to match. The Verilog spec provides C++ code as a reference definition for uniform random number generation. I assume that we would want to use that code, so that Bluesim matches. However, I have not checked whether various Verilog simulators give the same sequences. I note that Icarus Verilog and Verilator give a different number for $random()
called without a seed -- I have not yet tried with a seed, because my version of Verilator is rejecting it. I would also not be surprised if Verilator chose to ignore this part of the spec and just provide an arbitrary RNG, like calling random()
as you've done. It might be worth trying some of the commercial simulatirs (I'll see if I can).
Also, I'm unclear on how Bluesim would support a seed. In fact, BSC currently does nothing to guarantee that the seed argument is a state element or a Verilog variable, that can retain the RNG state after each call. It just so happens that if you pass a register, the generated Verilog will have a register there and it will work. If you call $random(17)
, though, you'll get illegal Verilog, that has a constant instead of a variable (both Icarus Verilog and Verlator will error). And I bet that you would get an error if the register module isn't inlined as an actual reg
in the code. (So BSC could be doing more to check the argument and generate proper code, even for Verilog. And it'd help if we had tests for these situations.)
For $value$plusargs
, you have to do something similar, because the function has a return value and it also needs to assign a value to one of its arguments. You've implemented this for Bluesim by changing some functions in ForeignFunctions.hs
.
I wonder, though, if BSC still needs to check that the argument is just a variable. What happens, say, if the design passes an expression as an argument -- does BSC reject that? This would be an error in the Verilog backend (which is expecting a signal name). But in Bluesim, could it end up creating pointers to odd locations? (Again, it would help to have some testcases for error situations, not just valid situations.)
I also think that BSC should be sanity checking the pattern argument, to check that it's well-formed and is one of the patterns that is supported. As mentioned on Issue #302, that may not be possible in TCheck
, and the check would need to be done after elaboration (but before the Verilog and Bluesim backends diverge).
It's really cool that you have something working, though! And limited support is better than no support. So I'm definitely OK with accepting a PR that has limits. But I mention in case you're able to address any of these concerns.
Sorry if that was a lot! I do appreciate the contribution and hope that we can get it merged!
src/Libraries/Base1/Prelude.bs
Outdated
@@ -3269,6 +3270,14 @@ foreign $fdisplayh :: PrimAction = "$fdisplayh" | |||
|
|||
foreign $random :: ActionValue_ 32 = "$random" | |||
|
|||
$urandom_range :: Bit 32 -> Maybe (Bit 32) -> ActionValue (Bit 32) |
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 support for variable arguments of system tasks is handled in TCheck
. The declared types of the foreign primitives here are ignored and replaced with special checking. For example, $random
can take zero or one argument, despite the declaration (a few lines above this) not mentioning an argument. In TCheck
, the function taskCheckRandom
is called to perform the checking and it looks for zero or one arguments. These taskCheck*
functions not only check the types but also perform any necessary transformations to create the actual implementations.
I suggest implementing $urandom_range
in the same way: Declare it here as simply ActionValue_ 32
and then add something like taskCheckURandomRange
in TCheck
.
Note that $random
is signed and the typechecking expects Int#(32)
(both for the return type and for the seed). If we're adding $urandom_range
, it might be worth adding $urandom
(which supports UInt#(32)
or possibly also Bit#(32)
).
src/bluesim/dollar_plusargs.cxx
Outdated
|
||
tUInt64 value = 0; | ||
switch (*(percent + 1)) { | ||
case 'd': |
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.
These don't match the Verilog/SystemVerilog spec. For example, from IEEE 1800-2017 (which I had on-hand), the patterns are:
%d decimal conversion
%o octal conversion
%h, %x hexadecimal conversion
%b binary conversion
%e real exponential conversion
%f real decimal conversion
%g real decimal or exponential conversion
%s string (no conversion)
and that both lowercase and uppercase are accepted, and leading 0
forms are valid.
It's OK to not accept %s
, %o
, or %b
for now. And it's probably OK to allow %e
, %f
, and %g
to accept all formats (exponentiatial or not). And probably OK not to support leading 0
. And probably OK if there are other corner cases that are not right (for example, does strtod
allow 0x
to appear in the string?).
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.
will change cxx to support all
src/bluesim/dollar_time.cxx
Outdated
@@ -11,3 +11,16 @@ tUInt32 dollar_stime(tSimStateHdl simHdl) | |||
{ | |||
return (tUInt32) bk_now(simHdl); | |||
} | |||
|
|||
tUInt32 dollar_random(tSimStateHdl simHdl) |
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.
As mentioned, these should go in a new file dollar_random.cxx
.
Also, they do not use tSimStateHdl
and should have the argument removed. There's code in ForeignFunctions.hs
that lists whether function needs this argument, and you'll also need to remove these functions from that list.
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.
made for testing, will split PR possibly for 3 in total
std::memcpy(&value, &d, sizeof(double)); | ||
break; | ||
} | ||
default: |
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.
If the format string isn't recognized, it might help to issue a warning. (But BSC should have reported something before generating code, before getting to this point.)
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.
during sim by bluesim the $value$plusargs would return False. same as in verilog sims
@@ -364,7 +364,8 @@ fnNeedsSimHdl name = any (`isPrefixOf` name ) tasks | |||
|
|||
, "$stop", "$finish" | |||
|
|||
, "$test$plusargs" | |||
, "$test$plusargs", "$value$plusargs" | |||
, "$random", "$urandom_range" |
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 is what tells BSC to provide the tSimStateHdl
argument. The random-tasks don't need it, so don't add them to this list.
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 for future use to keep rand seed which possible depends on sim(?)
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.
anyway, will remove that
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.
Oh, I see, you're correct. If $random
is called without a seed, the simulator needs to keep the seed state.
@@ -0,0 +1 @@ | |||
$value$plusargs is not given |
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.
Please make sure all the files have final newlines. GitHub's web interface shows that these expected files a missing the trailing newline.
conv :: Int -> AExpr -> Argument | ||
conv i e | ||
| is_valueplusargs && i == 1 = Ptr e | ||
| is_str e = Ptr 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.
Perhaps this should be:
conv i e
| is_valueplusargs && i == 1 = Ptr e
| otherwise = convAExprToArgument e
You've inlined convAExprToArgument
, which means now there's a copy that needs to be kept in sync, but also it looks like you may have accidentally deleted the arm for is_real
?
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.
as per
| otherwise = Arg e
would care about is_real, no?
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 see that I misspoke about about duplication. The function convAExprToArgument
was only being called from this one place.
And you are correct that removing the is_real
branch does not affect the functionality. However, sometimes it is helpful to keep such case arms, to make the code more self-documenting and/or less prone to errors when someone makes changes later. However, in this case, it may not be necessary. I am OK with removing the case arm (however, I am also OK with leaving it).
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.
will update as suggested - keep some branch
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.
Also, changing the name convAExprToArgument
to just conv loses some clarity of the purpose of the function. I would suggest making it at least convArg
.
sim = if add_sim then [SimHdl] else [] | ||
this = if add_this then [Module] else [] | ||
argstr = if (null args) then [] else [ArgStr] |
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 would prefer if we didn't make unnecessary changes to the code, like removing the parentheses in these two lines. When using git-blame to find the original source of some code, this introduces extra hops. Also, I think some people find the redundant parens to be more readable.
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.
sorry, what is better for these lines?
give some snippet
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'm suggesting to keep these two lines as they were (don't remove the parentheses):
argstr = if (null args) then [] else [ArgStr]
in sim ++ this ++ argstr ++ (maybeToList ret_arg) ++ rest
Ah, I see the following two failures in the CI:
Do these work for you? In both cases, Bluesim is parsing zeros:
|
weird.. |
I'm starting to think that Although I'm a little unsure of what the best way is to do that. The least invasive would be similar to how ActionValue system tasks work. For those, BSC divides the function into two parts, System tasks have an action component, but for system functions (that are just value functions), there is
would become this in Verilog:
and in Bluesim would be this:
When BSC creates the sequence of statements for a rule action, it starts with a list of actions and a set of defs and BSC inserts the defs where they need to be, according to dependings (after any inputs are computed but before any statements that need its outputs). For functions with multiple outputs, BSC would need to find the linked defs and treat them as one statement in that dependency analysis. (This happens in I think that is the least invasive. The alternative is to have one AFCall and one def, but then the type of that def would be a Pair (or a Maybe). That requires extending Anyway, I would prefer that we didn't introduce The |
A slight variant would be to have one |
what is this? looks ugly and unfamiliar to me. and impractical IMHO wire d1;
wire [7:0] d2;
assign d1 = $value$plusargs(..., d2);
// and no assign statement for d2 in Bluesim it's already as you mentioned ...
DEF_TASK_valueplusargs___d164((tUInt8)0u),
..
void MOD_mkTop::RL_testv_rule()
{
tUInt8 DEF_NOT_TASK_valueplusargs_64___d165;
tUInt64 DEF_y__h32454;
DEF_y__h32454 = INST_testv.METH_read();
DEF_TASK_valueplusargs___d164 = dollar_value_dollar_plusargs(sim_hdl,
"s,64p",
&__str_literal_5,
&DEF_y__h32454);
... the whole point to use value_plusargs is having sim (one executable file) able to run different e.g. seeds or different parameter injecting into sim env ( iverilog or vcs or xcelium or verilator) rule bla;
let a <- $value$plusargs("testF=%f",testv);
$display("testv F = %0x", testv);
Bit#(64) test64 = testv;//64'h123456789abcdef0;
Real testF = $bitstoreal(test64);
$display("testF = %02f",testF);
endrule
anyway, idea was to use either bluesim to sim or have same args to system calls in case of using verilog sim (commercial or open does not matter) and as soon as $value$plusargs has "predefined" arguments in its call I would rather have it same as other sims around = as is now |
I am confused. There are three things here: the input syntax, that then become an internal representation in BSC, and then the generated code (Bluesim or Verilog) that implements it. I am confused about which of these you are talking about. I am not proposing any change to the generated code. Sorry if it sounded like that. Ignore my examples! I did suggest a different input syntax, but I can be convinced to keep it unchanged (see below). My main concern is the internals of BSC. I want BSC to know what are outputs and what are inputs, when it does transformations and optimisations on the design. Right now, BSC thinks the argument is an input! And BSC just happens to generate code that works, but that is a fluke and is fragile and we should not rely on that. This is entirely independent of what the syntax looks like and what the generated code looks like, both of which we can make be whatever we want. I suggested that the syntax should return a Maybe type, but I can convinced to keep it as an argument. If we do that, though, the BSV parser needs to know that this system function is special; the parser needs to translate the argument into an internal representation that uses an output not an input -- so that the internals of BSC correctly know what are inputs and outputs.
I don't understand. That's an example of Verilog output from BSC and it should be identical to what your PR is generating, except maybe the
I know. I'm not proposing a different output. However, I'm pointing out that the the def
Can you explain this? If my output Verilog example was wrong, then ignore it -- I was concerned about the source BSV syntax and the internals of BSC; I was not trying to say anything about what it should generate. I also don't understand what you mean about not synthesisable. System functions aren't synthesisable anyway, only sim, are they not? In any case, I don't think I'm proposing any change to the output or to what is synthesisable. I am not suggesting that we can't use registers, if that's your concern?
I am not proposing to use initial blocks. I did not mention that at all. Did I say something that suggested that?
Yes, // 'testv' is declared with type Real
rule bla;
Bool a <- $value$plusargs("testF=%f",testv);
$display("testF = %02f",testF);
endrule Of course, it would be good to improve BSC to support more use of Real values in the generated hardware. For example, we can enhance BSC to allow By the way, this is what I'm proposing for the BSV-source syntax: rule bla;
Maybe#(Real) mresult = $value$plusargs("testF=%f");
case (mresult) matches
tagged Valid .v:
action
testv <= v;
$display("testF = %02f", v);
endaction
tagged Invalid:
action
$display("testF not found, using default");
testv <= 0;
// or consider aborting: $finish(0);
endaction
endcase
endrule In your rule, I notice that you don't check the return value |
Maybe it wasn't clear, and hopefully my last example illustrates it: I think that the type of |
idea to have same $value$plusargs as standard system task used to be in other sims. if bluesim has got all implemented PR's task - it is just good as I would test first in it, later in other sim. so, arg in functions/tasks are important .. at least to me )) tried this one
and
here's example what in generated verilog // register testv
reg [63 : 0] testv; //<--- REG! you can not assign to wire from plusargs AFAIK
wire [63 : 0] testv_D_IN;
wire testv_EN;
reg TASK_valueplusargs___d164; //<----------- same here!
//should be eiher reg or logic in case of SV
....
if (RST_N != `BSV_RESET_VALUE)
if (running &&
abort_whas__8_AND_abort_wget__9_0_OR_state_mkF_ETC___d154 &&
!start_reg)
$finish(32'd0);
begin
TASK_valueplusargs___d164 = $value$plusargs("testv=%d", testv);
#0;
end
if (TASK_valueplusargs___d164) $display("testv = %0x", testv); as per MayBe thing need more my time and testing |
@quark17 all in all, |
…rted and need another solutionj instead of strtoull
…aybe completely wrong
Added
usage of $value$plusargs