-
Notifications
You must be signed in to change notification settings - Fork 20
express device_implements_state_machine
using only run1
, without runUntilResp
(on top of #959)
#960
base: main
Are you sure you want to change the base?
Changes from all commits
554d6d9
7622478
5b28c68
f1e17a1
a5fec37
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,43 +56,52 @@ Class device_implements_state_machine{word: word.word 32}{mem: map.map word Byte | |
|
||
(* for each high-level state sH from which n bytes can be read at register r, | ||
if we run the low-level device with the read step's address on the input wires, | ||
we will get a response after at most device.maxRespDelay device cycles, | ||
and the response will match some possible high-level read step's response, | ||
it either tells us to try again later (but by decreasing device.maxRespDelay, | ||
it promises that it won't keep telling us to try again later forever), or | ||
we will get a response matching some possible high-level read step's response, | ||
but not necessarily the one we used to show that sH accepts reads (to allow | ||
underspecification-nondeterminism in the high-level state machine) *) | ||
state_machine_read_to_device_read: forall log2_nbytes r sH sL, | ||
state_machine_read_to_device_read_or_later: forall log2_nbytes r sH sL sL' h2d d2h, | ||
(exists v sH', state_machine.read_step (2 ^ log2_nbytes) sH r v sH') -> | ||
device_state_related sH sL -> | ||
exists d2h sL' sH', | ||
device.runUntilResp | ||
(set_a_valid true | ||
(set_a_opcode Get | ||
(set_a_size (N.of_nat log2_nbytes) | ||
(set_a_address (word_to_N (state_machine.reg_addr r)) | ||
(set_d_ready true tl_h2d_default))))) | ||
device.maxRespDelay sL = (Some d2h, sL') /\ | ||
device_state_related sH' sL' /\ | ||
state_machine.read_step (2 ^ log2_nbytes) sH r (N_to_word (d_data d2h)) sH'; | ||
a_valid h2d = true -> | ||
a_opcode h2d = Get -> | ||
a_size h2d = N.of_nat log2_nbytes -> | ||
a_address h2d = word_to_N (state_machine.reg_addr r) -> | ||
d_ready h2d = true -> | ||
device.run1 sL h2d = (sL', d2h) -> | ||
if d_valid d2h then | ||
exists sH', | ||
device_state_related sH' sL' /\ | ||
state_machine.read_step (2 ^ log2_nbytes) sH r (N_to_word (d_data d2h)) sH' | ||
else | ||
device_state_related sH sL' /\ | ||
(device.maxRespDelay sL' h2d < device.maxRespDelay sL h2d)%nat; | ||
Comment on lines
+67
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is incorrect in general (the original code also has the same issue). The process of reading from a register is broken down to two sub-tasks:
When you send an h2d packet on channel A you have to check that |
||
|
||
(* for each high-level state sH in which an n-byte write to register r with value v is possible, | ||
if we run the low-level device with the write step's address and value on the input wires, | ||
we will get an ack response after at most device.maxRespDelay device cycles, | ||
and the device will end up in a state corresponding to a high-level state reached after | ||
a high-level write, but not necessarily in the state we used to show that sH accepts writes *) | ||
state_machine_write_to_device_write: forall log2_nbytes r v sH sL, | ||
it either tells us to try again later (but by decreasing device.maxRespDelay, | ||
it promises that it won't keep telling us to try again later forever), or | ||
we will get an ack response and the device will end up in a state corresponding to a | ||
high-level state reached after a high-level write, but not necessarily in the state | ||
we used to show that sH accepts writes *) | ||
state_machine_write_to_device_write_or_later: forall log2_nbytes r v sH sL sL' h2d d2h, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar issue as with |
||
(exists sH', state_machine.write_step (2 ^ log2_nbytes) sH r v sH') -> | ||
device_state_related sH sL -> | ||
exists ignored sL' sH', | ||
device.runUntilResp | ||
(set_a_valid true | ||
(set_a_opcode PutFullData | ||
(set_a_size (N.of_nat log2_nbytes) | ||
(set_a_address (word_to_N (state_machine.reg_addr r)) | ||
(set_a_data (word_to_N v) | ||
(set_d_ready true tl_h2d_default)))))) | ||
device.maxRespDelay sL = (Some ignored, sL') /\ | ||
device_state_related sH' sL' /\ | ||
state_machine.write_step (2 ^ log2_nbytes) sH r v sH'; | ||
a_valid h2d = true -> | ||
a_opcode h2d = PutFullData -> | ||
a_size h2d = N.of_nat log2_nbytes -> | ||
a_address h2d = word_to_N (state_machine.reg_addr r) -> | ||
a_data h2d = word_to_N v -> | ||
d_ready h2d = true -> | ||
device.run1 sL h2d = (sL', d2h) -> | ||
if d_valid d2h then | ||
exists sH', | ||
device_state_related sH' sL' /\ | ||
state_machine.write_step (2 ^ log2_nbytes) sH r v sH' | ||
else | ||
device_state_related sH sL' /\ | ||
(device.maxRespDelay sL' h2d < device.maxRespDelay sL h2d)%nat; | ||
|
||
(* If two steps starting in the same high-level state agree on what gets appended to the trace, | ||
then the resulting high-level states must be equal. | ||
|
@@ -123,6 +132,84 @@ Section WithParams. | |
{D: device} | ||
{DI: device_implements_state_machine D M}. | ||
|
||
(* for each high-level state sH from which n bytes can be read at register r, | ||
if we run the low-level device with the read step's address on the input wires, | ||
we will get a response after at most device.maxRespDelay device cycles, | ||
and the response will match some possible high-level read step's response, | ||
but not necessarily the one we used to show that sH accepts reads (to allow | ||
underspecification-nondeterminism in the high-level state machine) *) | ||
Lemma state_machine_read_to_device_read: forall log2_nbytes r sH sL h2d, | ||
(exists v sH', state_machine.read_step (2 ^ log2_nbytes) sH r v sH') -> | ||
device_state_related sH sL -> | ||
a_valid h2d = true -> | ||
a_opcode h2d = Get -> | ||
a_size h2d = N.of_nat log2_nbytes -> | ||
a_address h2d = word_to_N (state_machine.reg_addr r) -> | ||
d_ready h2d = true -> | ||
exists d2h sL' sH', | ||
device.runUntilResp h2d (device.maxRespDelay sL h2d) sL = (Some d2h, sL') /\ | ||
device_state_related sH' sL' /\ | ||
state_machine.read_step (2 ^ log2_nbytes) sH r (N_to_word (d_data d2h)) sH'. | ||
Proof. | ||
intros. remember (device.maxRespDelay sL h2d) as fuel. remember (S fuel) as B. | ||
assert (device.maxRespDelay sL h2d <= fuel < B)%nat as HB by lia. clear HeqB Heqfuel. | ||
revert fuel sH sL H H0 HB. | ||
induction B; intros. | ||
- exfalso. lia. | ||
- destr fuel; cbn [device.runUntilResp]; destruct_one_match; | ||
pose proof (state_machine_read_to_device_read_or_later | ||
_ _ _ _ _ _ _ H H0 H1 H2 H3 H4 H5 E) as P; | ||
(destruct_one_match; [destruct P as (sH' & R & V) | destruct P as (R & Decr)]). | ||
+ (* 0 remaining fuel, valid response: *) | ||
clear -R V. eauto 10. | ||
+ (* 0 remaining fuel, no valid response: *) | ||
exfalso. lia. | ||
+ (* some remaining fuel, valid response: *) | ||
clear -R V. eauto 10. | ||
+ (* some remaining fuel, no valid response *) | ||
edestruct IHB as (d2h & sL'' & sH'' & Run & Rel & St). | ||
1: eassumption. 1: exact R. 2: eauto 10. lia. | ||
Qed. | ||
|
||
(* for each high-level state sH in which an n-byte write to register r with value v is possible, | ||
if we run the low-level device with the write step's address and value on the input wires, | ||
we will get an ack response after at most device.maxRespDelay device cycles, | ||
and the device will end up in a state corresponding to a high-level state reached after | ||
a high-level write, but not necessarily in the state we used to show that sH accepts writes *) | ||
Lemma state_machine_write_to_device_write: forall log2_nbytes r v sH sL h2d, | ||
(exists sH', state_machine.write_step (2 ^ log2_nbytes) sH r v sH') -> | ||
device_state_related sH sL -> | ||
a_valid h2d = true -> | ||
a_opcode h2d = PutFullData -> | ||
a_size h2d = N.of_nat log2_nbytes -> | ||
a_address h2d = word_to_N (state_machine.reg_addr r) -> | ||
a_data h2d = word_to_N v -> | ||
d_ready h2d = true -> | ||
exists ignored sL' sH', | ||
device.runUntilResp h2d (device.maxRespDelay sL h2d) sL = (Some ignored, sL') /\ | ||
device_state_related sH' sL' /\ | ||
state_machine.write_step (2 ^ log2_nbytes) sH r v sH'. | ||
Proof. | ||
intros. remember (device.maxRespDelay sL h2d) as fuel. remember (S fuel) as B. | ||
assert (device.maxRespDelay sL h2d <= fuel < B)%nat as HB by lia. clear HeqB Heqfuel. | ||
revert fuel sH sL H H0 HB. | ||
induction B; intros. | ||
- exfalso. lia. | ||
- destr fuel; cbn [device.runUntilResp]; destruct_one_match; | ||
pose proof (state_machine_write_to_device_write_or_later | ||
_ _ _ _ _ _ _ _ H H0 H1 H2 H3 H4 H5 H6 E) as P; | ||
(destruct_one_match; [destruct P as (sH' & R & V) | destruct P as (R & Decr)]). | ||
+ (* 0 remaining fuel, valid response: *) | ||
clear -R V. eauto 10. | ||
+ (* 0 remaining fuel, no valid response: *) | ||
exfalso. lia. | ||
+ (* some remaining fuel, valid response: *) | ||
clear -R V. eauto 10. | ||
+ (* some remaining fuel, no valid response *) | ||
edestruct IHB as (d2h & sL'' & sH'' & Run & Rel & St). | ||
1: eassumption. 1: exact R. 2: eauto 10. lia. | ||
Qed. | ||
|
||
Inductive related: MetricRiscvMachine -> ExtraRiscvMachine D -> Prop := | ||
mkRelated: forall regs pc npc m xAddrs (t: list LogItem) t_ignored mc d s, | ||
execution t s -> | ||
|
@@ -314,11 +401,15 @@ Section WithParams. | |
| E1: execution _ _, E2: execution _ _ |- _ => | ||
pose proof (execution_unique _ _ _ E1 E2); subst; clear E2 | ||
end. | ||
1-4: edestruct state_machine_read_to_device_read as (v'' & d'' & s'' & RU'' & Rel'' & RS''); | ||
[do 2 eexists; match goal with | ||
| H: state_machine.read_step ?n _ _ _ _ |- _ => | ||
change n at 1 with (2 ^ (Nat.log2 n))%nat in H | ||
end; eassumption|solve[eauto]|]. | ||
1-4: match goal with | ||
| |- context[device.runUntilResp ?p _ _] => | ||
edestruct state_machine_read_to_device_read with (h2d := p) | ||
as (v'' & d'' & s'' & RU'' & Rel'' & RS''); | ||
[do 2 eexists; match goal with | ||
| H: state_machine.read_step ?n _ _ _ _ |- _ => | ||
change n at 1 with (2 ^ (Nat.log2 n))%nat in H | ||
end; eassumption|eassumption|reflexivity..|] | ||
end. | ||
1-4: cbn -[HList.tuple]; tlsimpl; simpl in RU''; rewrite RU''; cbn -[HList.tuple]. | ||
4: { (* 64-bit MMIO is not supported: *) | ||
eapply state_machine.read_step_size_valid in HLp2p2. simpl in HLp2p2. exfalso. intuition congruence. | ||
|
@@ -349,13 +440,19 @@ Section WithParams. | |
| E1: execution _ _, E2: execution _ _ |- _ => | ||
pose proof (execution_unique _ _ _ E1 E2); subst; clear E2 | ||
end. | ||
1-3: edestruct state_machine_write_to_device_write as (ignored & d' & s'' & RU & Rel' & WS'); | ||
[eexists; match goal with | ||
| H: state_machine.write_step ?n _ _ _ _ |- _ => | ||
change n at 1 with (2 ^ (Nat.log2 n))%nat in H | ||
end; eassumption|solve[eauto]|]. | ||
1-3: match goal with | ||
| |- context[device.runUntilResp ?p _ _] => | ||
edestruct state_machine_write_to_device_write with (h2d := p) | ||
as (ignored & d' & s'' & RU & Rel' & WS'); | ||
[eexists; match goal with | ||
| H: state_machine.write_step ?n _ _ _ _ |- _ => | ||
change n at 1 with (2 ^ (Nat.log2 n))%nat in H | ||
end; eassumption | ||
|eassumption | ||
|rewrite ? Z_word_N in * by lia; try reflexivity..] | ||
end. | ||
1-3: cbn -[HList.tuple Primitives.invalidateWrittenXAddrs]; | ||
tlsimpl; simpl in RU; rewrite Z_word_N in RU by lia; rewrite RU; | ||
tlsimpl; simpl in RU; rewrite RU; | ||
cbn -[HList.tuple Primitives.invalidateWrittenXAddrs]. | ||
1-3: eauto 15 using mkRelated, execution_write_cons, preserve_disjoint_of_invalidateXAddrs. | ||
|
||
|
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 not sure if this is relevant, but the SW semantics allows reading the value register only when in the DONE state.