Skip to content

Commit 0bbf258

Browse files
authored
Merge pull request #513 from OpenVADL/feature/cosim-mixed-endianness
iss: Added support for mixing simulators with different endianness
2 parents b7dbcbe + cf57ffa commit 0bbf258

File tree

12 files changed

+529
-157
lines changed

12 files changed

+529
-157
lines changed

vadl-cosim/broker/src/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ fn run(config: Config) -> Result<()> {
9393
let mut buf = String::new();
9494
if report_data.passed {
9595
buf.push_str("Cosimulation passed!");
96+
for client in broker.clients() {
97+
let s = format!(
98+
"\n\t\"{}\" executed {} steps",
99+
client.name.clone().unwrap_or(client.id.to_string()),
100+
client.run_count
101+
);
102+
buf.push_str(&s);
103+
}
96104
} else {
97105
add_plain_report_summary(&mut buf, &report_data);
98106
}

vadl-cosim/cosim-lib/src/config/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,17 @@ pub struct Client {
178178
pub skip_n_instructions: u32,
179179

180180
pub name: Option<String>,
181+
182+
#[serde(default = "Endian::default")]
183+
pub endian: Endian,
184+
}
185+
186+
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
187+
#[serde(rename_all = "lowercase")]
188+
pub enum Endian {
189+
#[default]
190+
Big,
191+
Little,
181192
}
182193

183194
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -227,6 +238,13 @@ impl Qemu {
227238
.map(|(k, v)| (v.clone(), k.clone()))
228239
.collect();
229240
}
241+
242+
pub fn has_equal_endianess(&self) -> bool {
243+
match &self.clients[..] {
244+
[] => true,
245+
[head, tail @ ..] => tail.iter().all(|c| c.endian == head.endian),
246+
}
247+
}
230248
}
231249

232250
#[derive(Debug, Serialize, Deserialize, Clone)]

vadl-cosim/cosim-lib/src/cosim/mod.rs

Lines changed: 172 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ use crate::{
1212
get_all_clients_contexts_before, get_all_clients_contexts_current,
1313
get_all_clients_instructions,
1414
},
15-
ipc::{cstructs, qemu::Client},
15+
ipc::{
16+
cstructs::{self, TBInfo, TBInsnInfo},
17+
qemu::Client,
18+
},
1619
trace::{TraceEntryData, TraceStore, connect, get_client_trace, store_trace, trace_collect},
1720
};
1821

@@ -90,6 +93,10 @@ impl Broker {
9093
Ok(())
9194
}
9295

96+
pub fn clients(&self) -> &Vec<Client> {
97+
&self.clients
98+
}
99+
93100
fn run_lockstep(&mut self, config: &Config) -> Result<Report> {
94101
// NOTE: maybe move "spawning" the clients into this method
95102
for (idx, client) in self.clients.iter_mut().enumerate() {
@@ -114,7 +121,7 @@ impl Broker {
114121
.clients
115122
.iter_mut()
116123
.map(|client| {
117-
let res = client.shm.read_buffer().map(|i| i.as_insn());
124+
let res = client.shm.read_buffer().map(|opt| opt.map(|i| i.as_insn()));
118125
client.run_count += 1;
119126
res
120127
})
@@ -123,72 +130,144 @@ impl Broker {
123130
let c1insn = reads[0];
124131
let c2insn = reads[1];
125132

126-
let diffs = diff_cpus(
127-
&c1insn.cpus,
128-
c1insn.init_mask,
129-
&c2insn.cpus,
130-
c2insn.init_mask,
131-
config,
132-
);
133-
134-
self.trace_clients(config)?;
135-
136-
if !diffs.is_empty() {
137-
let ctx = self.build_diff_context(config)?;
138-
return Ok(Report::failed(diffs, ctx));
139-
}
140-
141-
for client in &mut self.clients {
142-
client.shm.end_read_buffer();
143-
}
133+
match (c1insn, c2insn) {
134+
// successfully read both clients -> compare
135+
(Some(c1insn), Some(c2insn)) => {
136+
let diffs = diff_cpus(
137+
&c1insn.cpus,
138+
c1insn.init_mask,
139+
&c2insn.cpus,
140+
c2insn.init_mask,
141+
config,
142+
);
143+
144+
self.trace_clients(config)?;
145+
146+
if !diffs.is_empty() {
147+
let ctx = self.build_diff_context(config)?;
148+
return Ok(Report::failed(diffs, ctx));
149+
}
150+
151+
for client in &mut self.clients {
152+
client.shm.end_read_buffer();
153+
}
154+
155+
if !config.testing.protocol.execute_all_remaining_instructions {
156+
stop_after -= 1;
157+
if stop_after == 0 {
158+
break;
159+
}
160+
}
161+
}
144162

145-
if !config.testing.protocol.execute_all_remaining_instructions {
146-
stop_after -= 1;
147-
if stop_after == 0 {
148-
break;
163+
// both clients finished at the same time => stop cosimulation
164+
(None, None) => break,
165+
166+
// one client finished while the other still writes to the buffer, error state!
167+
(Some(c1insn), None) => {
168+
let diff = DiffEntry::new(
169+
"invalid-execution",
170+
vec![Broker::format_insn_for_diff(&c1insn.insn_info)],
171+
Broker::format_invalid_execution_client_msg(
172+
&self.clients[0],
173+
&self.clients[1],
174+
),
175+
);
176+
let ctx = self.build_diff_context(config)?;
177+
return Ok(Report::failed(vec![diff], ctx));
178+
}
179+
(None, Some(c2insn)) => {
180+
let diff = DiffEntry::new(
181+
"invalid-execution",
182+
vec![Broker::format_insn_for_diff(&c2insn.insn_info)],
183+
Broker::format_invalid_execution_client_msg(
184+
&self.clients[1],
185+
&self.clients[0],
186+
),
187+
);
188+
let ctx = self.build_diff_context(config)?;
189+
return Ok(Report::failed(vec![diff], ctx));
149190
}
150191
}
151192
},
152193
crate::config::ProtocolLayer::TB | crate::config::ProtocolLayer::TBStrict => loop {
153194
let reads = self
154195
.clients
155196
.iter_mut()
156-
.map(|client| client.shm.read_buffer().map(|i| i.as_tb()))
197+
.map(|client| {
198+
let res = client.shm.read_buffer().map(|opt| opt.map(|i| i.as_tb()));
199+
client.run_count += 1;
200+
res
201+
})
157202
.collect::<Result<Vec<_>>>()?;
158203

159204
let c1insn = reads[0];
160205
let c2insn = reads[1];
161206

162-
if let TBSyncResult::Diverged(diff_entry) =
163-
Self::check_if_clients_are_synchronized(&[c1insn, c2insn])
164-
{
165-
debug!("client diverged during tb synchronization");
166-
return Ok(Report::failed(vec![diff_entry], vec![]));
167-
}
168-
169-
let diffs = diff_cpus(
170-
&c1insn.cpus,
171-
c1insn.init_mask,
172-
&c2insn.cpus,
173-
c2insn.init_mask,
174-
config,
175-
);
176-
177-
self.trace_clients(config)?;
178-
179-
if !diffs.is_empty() {
180-
let ctx = self.build_diff_context(config)?;
181-
return Ok(Report::failed(diffs, ctx));
182-
}
183-
184-
for client in &mut self.clients {
185-
client.shm.end_read_buffer();
186-
}
207+
match (c1insn, c2insn) {
208+
// successfully read both clients -> compare
209+
(Some(c1insn), Some(c2insn)) => {
210+
if let TBSyncResult::Diverged(diff_entry) =
211+
Self::check_if_clients_are_synchronized(&[c1insn, c2insn])
212+
{
213+
debug!("client diverged during tb synchronization");
214+
return Ok(Report::failed(vec![diff_entry], vec![]));
215+
}
216+
217+
let diffs = diff_cpus(
218+
&c1insn.cpus,
219+
c1insn.init_mask,
220+
&c2insn.cpus,
221+
c2insn.init_mask,
222+
config,
223+
);
224+
225+
self.trace_clients(config)?;
226+
227+
if !diffs.is_empty() {
228+
let ctx = self.build_diff_context(config)?;
229+
return Ok(Report::failed(diffs, ctx));
230+
}
231+
232+
for client in &mut self.clients {
233+
client.shm.end_read_buffer();
234+
}
235+
236+
if !config.testing.protocol.execute_all_remaining_instructions {
237+
stop_after -= 1;
238+
if stop_after == 0 {
239+
break;
240+
}
241+
}
242+
}
187243

188-
if !config.testing.protocol.execute_all_remaining_instructions {
189-
stop_after -= 1;
190-
if stop_after == 0 {
191-
break;
244+
// both clients finished at the same time => stop cosimulation
245+
(None, None) => break,
246+
247+
// one client finished while the other still writes to the buffer, error state!
248+
(Some(c1tb), None) => {
249+
let diff = DiffEntry::new(
250+
"invalid-execution",
251+
vec![Broker::format_tb_for_diff(&c1tb.tb_info)],
252+
Broker::format_invalid_execution_client_msg(
253+
&self.clients[0],
254+
&self.clients[1],
255+
),
256+
);
257+
let ctx = self.build_diff_context(config)?;
258+
return Ok(Report::failed(vec![diff], ctx));
259+
}
260+
(None, Some(c2tb)) => {
261+
let diff = DiffEntry::new(
262+
"invalid-execution",
263+
vec![Broker::format_tb_for_diff(&c2tb.tb_info)],
264+
Broker::format_invalid_execution_client_msg(
265+
&self.clients[1],
266+
&self.clients[0],
267+
),
268+
);
269+
let ctx = self.build_diff_context(config)?;
270+
return Ok(Report::failed(vec![diff], ctx));
192271
}
193272
}
194273
},
@@ -197,6 +276,45 @@ impl Broker {
197276
Ok(Report::passed())
198277
}
199278

279+
fn format_insn_for_diff(insn: &TBInsnInfo) -> String {
280+
format!(
281+
"pc={}, size={}, symbol={}, hwaddr={}, disas={}, data={}",
282+
insn.pc,
283+
insn.size,
284+
insn.symbol.as_str(),
285+
insn.hwaddr.as_str(),
286+
insn.disas.as_str(),
287+
insn.data.buffer_slice_fmt(),
288+
)
289+
}
290+
291+
fn format_tb_for_diff(tb: &TBInfo) -> String {
292+
let insns = tb
293+
.insns_info_slice()
294+
.iter()
295+
.map(Broker::format_insn_for_diff)
296+
.collect::<Vec<_>>();
297+
298+
format!("pc={}, insns={:#?}", tb.pc, insns)
299+
}
300+
301+
fn format_invalid_execution_client_msg(
302+
executing_client: &Client,
303+
halted_client: &Client,
304+
) -> String {
305+
format!(
306+
"client \"{}\" executed another instruction while client \"{}\" has already finished",
307+
executing_client
308+
.name
309+
.as_deref()
310+
.unwrap_or(&executing_client.id.to_string()),
311+
halted_client
312+
.name
313+
.as_deref()
314+
.unwrap_or(&halted_client.id.to_string())
315+
)
316+
}
317+
200318
fn check_clients_are_initially_synchronized(&self, config: &Config) -> Result<()> {
201319
let start_pcs = self
202320
.clients

vadl-cosim/cosim-lib/src/diff/diff.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,23 @@ pub fn diff_register(
122122
));
123123
}
124124

125-
if reg1.data_slice() != reg2.data_slice() {
126-
diffs.push(DiffEntry::new(
127-
format!("cpu[{cpu_index}].registers[{reg_index}].data"),
128-
vec![reg1.data_slice_fmt(), reg2.data_slice_fmt()],
129-
format!("different register data for {r1name}"),
130-
));
125+
for idx in 0..(reg1.size as usize) {
126+
let d1 = reg1.data_slice()[idx];
127+
let d2 = if config.qemu.has_equal_endianess() {
128+
reg2.data_slice()[idx]
129+
} else {
130+
reg2.data_slice()[(reg2.size as usize) - idx - 1]
131+
};
132+
133+
if d1 != d2 {
134+
diffs.push(DiffEntry::new(
135+
format!("cpu[{cpu_index}].registers[{reg_index}].data"),
136+
vec![reg1.data_slice_fmt(), reg2.data_slice_fmt()],
137+
format!("different register data for {r1name}"),
138+
));
139+
140+
break;
141+
}
131142
}
132143
}
133144

vadl-cosim/cosim-lib/src/diff/mod.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt::Debug;
22

3+
use anyhow::anyhow;
34
use serde::Serialize;
45

56
use crate::{
@@ -210,11 +211,27 @@ pub fn get_client_context_current(
210211
) -> anyhow::Result<DiffContextClientState> {
211212
match config.testing.protocol.layer {
212213
crate::config::ProtocolLayer::Insn => {
213-
let ctx = (client.shm.read_buffer()?.as_insn(), config).into();
214+
let ctx = (
215+
client
216+
.shm
217+
.read_buffer()?
218+
.ok_or(anyhow!("expected to be able to read ringbuffer"))?
219+
.as_insn(),
220+
config,
221+
)
222+
.into();
214223
Ok(ctx)
215224
}
216225
crate::config::ProtocolLayer::TB | crate::config::ProtocolLayer::TBStrict => {
217-
let ctx = (client.shm.read_buffer()?.as_tb(), config).into();
226+
let ctx = (
227+
client
228+
.shm
229+
.read_buffer()?
230+
.ok_or(anyhow!("expected to be able to read ringbuffer"))?
231+
.as_tb(),
232+
config,
233+
)
234+
.into();
218235
Ok(ctx)
219236
}
220237
}

0 commit comments

Comments
 (0)