1use celox_design::{BitAccess, StateAddr as AbsoluteAddr, VarAtomBase};
2use celox_testbench::{
3 AssertMessage as GenericAssertMessage, ClockCount as GenericClockCount, CompiledExpr,
4 ExecutableArgument, ExecutableAssertMessage, ExecutableClockCount, ExecutableLoopBound,
5 ExecutableStatement, ExecutableTestbench, ExprBytecode, LoopBound as GenericLoopBound,
6 SemanticArgument, SemanticComponentBinding, SemanticStatement, StateLocation, TestbenchProgram,
7 TestbenchSelection, TestbenchStatement as GenericTestbenchStatement, TestbenchTarget,
8};
9
10use crate::{SignalRef, backend::SimBackend};
11
12fn bind_expr<B: SimBackend>(
13 backend: &B,
14 expr: ExprBytecode<StateLocation<AbsoluteAddr>>,
15) -> Option<CompiledExpr> {
16 let layout = backend.layout();
17 let bytecode = expr
18 .bind_with(|address| layout.offsets.get(address).copied())
19 .ok()?;
20 Some(CompiledExpr::new(bytecode))
21}
22
23fn bind_component<B: SimBackend>(
24 backend: &B,
25 component: SemanticComponentBinding<AbsoluteAddr>,
26 rtl_writes: &fxhash::FxHashSet<VarAtomBase<AbsoluteAddr>>,
27) -> Option<celox_testbench::ExecutableComponentBinding<B::Event, SignalRef>> {
28 Some(celox_testbench::ComponentBinding {
29 instance: component.instance,
30 connections: component
31 .connections
32 .into_iter()
33 .map(|connection| {
34 let output = match connection.output {
35 Some(output) => Some(bind_target(backend, output)?),
36 None => None,
37 };
38 let output_rtl_driven = output.as_ref().is_some_and(|output| {
39 let target_access = match &output.selection {
40 Some(selection) => selection
41 .offset
42 .constant_u64()
43 .and_then(|offset| usize::try_from(offset).ok())
44 .and_then(|lsb| {
45 output
46 .width
47 .checked_sub(1)
48 .and_then(|tail| lsb.checked_add(tail))
49 .map(|msb| BitAccess::new(lsb, msb))
50 }),
51 None => output
52 .signal
53 .width
54 .checked_sub(1)
55 .map(|msb| BitAccess::new(0, msb)),
56 };
57 rtl_writes.iter().any(|write| {
58 backend.resolve_signal(&write.id) == output.signal
59 && target_access.is_none_or(|target| target.overlaps(&write.access))
60 })
61 });
62 Some(celox_testbench::ComponentConnectionBinding {
63 port: connection.port,
64 input: match connection.input {
65 Some(input) => Some(bind_expr(backend, input)?),
66 None => None,
67 },
68 input_target: match connection.input_target {
69 Some(input) => Some(bind_target(backend, input)?),
70 None => None,
71 },
72 output,
73 output_rtl_driven,
74 event: connection
75 .event
76 .and_then(|event| backend.resolve_event_opt(&event)),
77 })
78 })
79 .collect::<Option<Vec<_>>>()?,
80 })
81}
82
83fn bind_assert_arg<B: SimBackend>(
84 backend: &B,
85 arg: SemanticArgument<AbsoluteAddr>,
86) -> Option<ExecutableArgument> {
87 Some(ExecutableArgument {
88 expr: bind_expr(backend, arg.expr)?,
89 width: arg.width,
90 signed: arg.signed,
91 is_string: arg.is_string,
92 })
93}
94
95fn bind_assert_message<B: SimBackend>(
96 backend: &B,
97 message: GenericAssertMessage<SemanticArgument<AbsoluteAddr>>,
98) -> Option<ExecutableAssertMessage> {
99 match message {
100 GenericAssertMessage::Formatted { template, args } => {
101 let args = args
102 .into_iter()
103 .map(|arg| bind_assert_arg(backend, arg))
104 .collect::<Option<Vec<_>>>()?;
105 Some(GenericAssertMessage::Formatted { template, args })
106 }
107 GenericAssertMessage::DynamicArgs(args) => {
108 let args = args
109 .into_iter()
110 .map(|arg| bind_assert_arg(backend, arg))
111 .collect::<Option<Vec<_>>>()?;
112 Some(GenericAssertMessage::DynamicArgs(args))
113 }
114 }
115}
116
117fn bind_clock_count<B: SimBackend>(
118 backend: &B,
119 count: GenericClockCount<ExprBytecode<StateLocation<AbsoluteAddr>>>,
120) -> Option<ExecutableClockCount> {
121 match count {
122 GenericClockCount::Static(count) => Some(GenericClockCount::Static(count)),
123 GenericClockCount::Dynamic(expr) => {
124 Some(GenericClockCount::Dynamic(bind_expr(backend, expr)?))
125 }
126 }
127}
128
129fn bind_loop_bound<B: SimBackend>(
130 backend: &B,
131 bound: GenericLoopBound<ExprBytecode<StateLocation<AbsoluteAddr>>>,
132) -> Option<ExecutableLoopBound> {
133 match bound {
134 GenericLoopBound::Static(bound) => Some(GenericLoopBound::Static(bound)),
135 GenericLoopBound::Dynamic {
136 expr,
137 width,
138 signed,
139 } => Some(GenericLoopBound::Dynamic {
140 expr: bind_expr(backend, expr)?,
141 width,
142 signed,
143 }),
144 }
145}
146
147fn bind_target<B: SimBackend>(
148 backend: &B,
149 target: TestbenchTarget<
150 celox_testbench::SemanticSignal<AbsoluteAddr>,
151 ExprBytecode<StateLocation<AbsoluteAddr>>,
152 >,
153) -> Option<TestbenchTarget<SignalRef, CompiledExpr>> {
154 Some(TestbenchTarget {
155 signal: backend.resolve_signal(&target.signal.address),
156 selection: match target.selection {
157 Some(selection) => Some(TestbenchSelection {
158 offset: bind_expr(backend, selection.offset)?,
159 width: selection.width,
160 }),
161 None => None,
162 },
163 width: target.width,
164 })
165}
166
167fn bind_optional_target<B: SimBackend>(
168 backend: &B,
169 target: Option<
170 TestbenchTarget<
171 celox_testbench::SemanticSignal<AbsoluteAddr>,
172 ExprBytecode<StateLocation<AbsoluteAddr>>,
173 >,
174 >,
175) -> Option<Option<TestbenchTarget<SignalRef, CompiledExpr>>> {
176 match target {
177 Some(target) => Some(Some(bind_target(backend, target)?)),
178 None => Some(None),
179 }
180}
181
182fn bind_statement<B: SimBackend>(
183 backend: &B,
184 statement: SemanticStatement<AbsoluteAddr>,
185) -> Option<ExecutableStatement<B::Event, SignalRef>> {
186 match statement {
187 GenericTestbenchStatement::ClockNext { clock_event, count } => {
188 Some(GenericTestbenchStatement::ClockNext {
189 clock_event: backend.resolve_event_opt(&clock_event)?,
190 count: bind_clock_count(backend, count)?,
191 })
192 }
193 GenericTestbenchStatement::ResetAssert {
194 reset_signal,
195 reset_event,
196 clock_event,
197 duration,
198 assert_value,
199 deassert_value,
200 } => Some(GenericTestbenchStatement::ResetAssert {
201 reset_signal: backend.resolve_signal(&reset_signal.address),
202 reset_event: reset_event.and_then(|event| backend.resolve_event_opt(&event)),
203 clock_event: backend.resolve_event_opt(&clock_event)?,
204 duration: bind_clock_count(backend, duration)?,
205 assert_value,
206 deassert_value,
207 }),
208 GenericTestbenchStatement::Assert {
209 expr,
210 site_id,
211 continue_on_fail,
212 message,
213 location,
214 } => Some(GenericTestbenchStatement::Assert {
215 expr: bind_expr(backend, expr)?,
216 site_id,
217 continue_on_fail,
218 message: match message {
219 Some(message) => Some(bind_assert_message(backend, message)?),
220 None => None,
221 },
222 location,
223 }),
224 GenericTestbenchStatement::Display { message, newline } => {
225 Some(GenericTestbenchStatement::Display {
226 message: match message {
227 Some(message) => Some(bind_assert_message(backend, message)?),
228 None => None,
229 },
230 newline,
231 })
232 }
233 GenericTestbenchStatement::If {
234 expr,
235 then_block,
236 else_block,
237 } => Some(GenericTestbenchStatement::If {
238 expr: bind_expr(backend, expr)?,
239 then_block: then_block
240 .into_iter()
241 .map(|statement| bind_statement(backend, statement))
242 .collect::<Option<Vec<_>>>()?,
243 else_block: else_block
244 .into_iter()
245 .map(|statement| bind_statement(backend, statement))
246 .collect::<Option<Vec<_>>>()?,
247 }),
248 GenericTestbenchStatement::For {
249 loop_var,
250 start,
251 end,
252 inclusive,
253 step,
254 step_op,
255 reverse,
256 body,
257 } => Some(GenericTestbenchStatement::For {
258 loop_var: loop_var.map(|(signal, width, signed)| {
259 (backend.resolve_signal(&signal.address), width, signed)
260 }),
261 start: bind_loop_bound(backend, start)?,
262 end: bind_loop_bound(backend, end)?,
263 inclusive,
264 step,
265 step_op,
266 reverse,
267 body: body
268 .into_iter()
269 .map(|statement| bind_statement(backend, statement))
270 .collect::<Option<Vec<_>>>()?,
271 }),
272 GenericTestbenchStatement::Assign { dst, expr } => {
273 Some(GenericTestbenchStatement::Assign {
274 dst: bind_target(backend, dst)?,
275 expr: bind_expr(backend, expr)?,
276 })
277 }
278 GenericTestbenchStatement::RandomSeed { handle, value } => {
279 Some(GenericTestbenchStatement::RandomSeed {
280 handle,
281 value: bind_expr(backend, value)?,
282 })
283 }
284 GenericTestbenchStatement::RandomGet {
285 handle,
286 width,
287 signed,
288 ret,
289 } => Some(GenericTestbenchStatement::RandomGet {
290 handle,
291 width,
292 signed,
293 ret: bind_optional_target(backend, ret)?,
294 }),
295 GenericTestbenchStatement::RandomGetRange {
296 handle,
297 min,
298 max,
299 width,
300 signed,
301 ret,
302 } => Some(GenericTestbenchStatement::RandomGetRange {
303 handle,
304 min: bind_expr(backend, min)?,
305 max: bind_expr(backend, max)?,
306 width,
307 signed,
308 ret: bind_optional_target(backend, ret)?,
309 }),
310 GenericTestbenchStatement::RandomGetSeed { handle, ret } => {
311 Some(GenericTestbenchStatement::RandomGetSeed {
312 handle,
313 ret: bind_optional_target(backend, ret)?,
314 })
315 }
316 GenericTestbenchStatement::ComponentMethod {
317 instance,
318 method,
319 args,
320 ret,
321 ret_width,
322 ret_signed,
323 ret_strict,
324 } => Some(GenericTestbenchStatement::ComponentMethod {
325 instance,
326 method,
327 args: args
328 .into_iter()
329 .map(|arg| bind_assert_arg(backend, arg))
330 .collect::<Option<Vec<_>>>()?,
331 ret: bind_optional_target(backend, ret)?,
332 ret_width,
333 ret_signed,
334 ret_strict,
335 }),
336 GenericTestbenchStatement::Break => Some(GenericTestbenchStatement::Break),
337 GenericTestbenchStatement::Finish => Some(GenericTestbenchStatement::Finish),
338 }
339}
340
341pub fn bind_testbench_program<B: SimBackend>(
342 backend: &B,
343 program: TestbenchProgram<AbsoluteAddr>,
344 rtl_writes: &fxhash::FxHashSet<VarAtomBase<AbsoluteAddr>>,
345) -> Option<ExecutableTestbench<B::Event, SignalRef>> {
346 let random_seed = program.configured_random_seed();
347 let components = program.components().to_vec();
348 let component_libraries = program.component_libraries().to_vec();
349 let component_file_base = program.component_file_base().map(ToOwned::to_owned);
350 let component_bindings = program
351 .component_bindings()
352 .to_vec()
353 .into_iter()
354 .map(|component| bind_component(backend, component, rtl_writes))
355 .collect::<Option<Vec<_>>>()?;
356 let statements = program
357 .into_statements()
358 .into_iter()
359 .map(|statement| bind_statement(backend, statement))
360 .collect::<Option<Vec<_>>>()?;
361 Some(
362 ExecutableTestbench::new_with_random_seed(statements, random_seed).with_component_runtime(
363 components,
364 component_libraries,
365 component_file_base,
366 component_bindings,
367 ),
368 )
369}