I think I see what you're getting at here. requestTag
followed by retireTag
leads to a different end state than retireTag
followed by requestTag
...
That is true, but that is also true about my example with two registers above, and the one-rule version works fine.
But why?
There are two ways to compose the one := other + 1
actions sequentially (i.e., in different rules) — which are different — and there is a third way to compose the two actions calls in parallel which gives a different result from both of the sequential ones.
Why? Well, if you look at the scheduling annotation for vMkReg
in Prelude.bs
you'll see that read < write
for a Reg. In a single rule this is fine: both reads happen first and then both writes. You can describe it like this: read < read < write < write
.
But if you put them in two different rules, the scheduler can't do that anymore. To keep the rules atomic, it needs to order them read < write < read < write
(where the first two are from one rule and the second two from the other). If you wanted to have the same behaviour as the one-rule version, you would have to interleave method calls from both rules, which would break atomicity, right?
An acceptable alternative is we don't have to return the freshly retired tag for the edge case... We could instead return an older free tag...
Yeah, this is one of the options that I was suggesting.
The downside of this is the pathological case where there are no free tags - then we can't retire and request simultaneously. This would imply a system that requests tags more quickly than it retires them... So this would be a load balancing issue rather than an issue with the TagEngine.
Not necessarily, it just implies a system that needs all of the tags to be in flight at some point — which, you could argue, if you don't need then why do you have so many tags — and can also request/retire concurrently when all of the tags are in flight. (But, on the other hand, you could also argue that one more tag than the number of requests you can service never hurt anybody, since you probably need one extra thing or a valid bit to indicate empty somewhere anyway, etc etc.)
It does, however, also imply a combinational path between the retiring tag and the request, which you might or might not want depending on how many “clients” this tag engine has, what your timing constraints are, what your PD partitions are, and so on. At some scale, I think you actually want the behaviour that doesn't require this combinational path regardless.