1use celox_design::{
4 BinaryOp, BitAccess, DomainKind, InitialStateData, InitialStateValue, ModuleId, PortTypeKind,
5 RegionedVarAddrBase, STABLE_REGION, TriggerSet, UnaryOp, VarAtomBase, VariableMetadata,
6 WORKING_REGION,
7};
8use celox_frontend_sdk::{
9 ActiveLevel, Direction, Edge, ExprId, ExprNode, FrontendArtifact, SignalId, SignalSlice,
10 ValueType,
11};
12use celox_sir::{
13 BlockId, ExecutionUnit, RegisterId, SIRBuilder, SIRInstruction, SIROffset, SIRTerminator,
14 SIRValue, merge_sir_eus,
15};
16use celox_slt::{LogicPath, LogicPathTarget, NodeId, SLTNode, SLTNodeArena};
17use thiserror::Error;
18
19use crate::symbolic::artifact::{
20 ExternalHierarchy, ExternalModule, SimModule, SymbolicRtl, SymbolicVariable,
21};
22use crate::symbolic::width::coerce_node_width;
23use crate::{HashMap, HashSet, SourceVarId, VariableKind};
24
25type RegionedSourceAddr = RegionedVarAddrBase<SourceVarId>;
26
27pub struct LoweredFrontendArtifact {
33 pub symbolic: SymbolicRtl,
34 pub external: ExternalHierarchy,
35}
36
37#[derive(Debug, Error)]
38pub enum FrontendArtifactError {
39 #[error("invalid frontend artifact: {0}")]
40 Validation(#[from] celox_frontend_sdk::BuildError),
41 #[error("frontend artifact references unknown signal {0}")]
42 UnknownSignal(u32),
43 #[error("frontend artifact references unknown expression {0}")]
44 UnknownExpression(u32),
45 #[error("unsupported frontend SDK expression or operation")]
46 UnsupportedOperation,
47 #[error("signal `{signal}` is used with conflicting clock/reset roles")]
48 ConflictingSignalRole { signal: String },
49 #[error(
50 "async reset `{reset}` is shared by distinct clock domains `{first_clock}` and `{second_clock}`"
51 )]
52 SharedResetAcrossClocks {
53 reset: String,
54 first_clock: String,
55 second_clock: String,
56 },
57 #[error("frontend SDK expression is invalid: {0}")]
58 InvalidExpression(#[from] celox_slt::SLTNodeFactsError),
59}
60
61fn source_id(id: SignalId) -> SourceVarId {
62 SourceVarId(id.index())
63}
64
65fn signal_atom(slice: SignalSlice) -> VarAtomBase<SourceVarId> {
66 VarAtomBase::new(
67 source_id(slice.signal()),
68 slice.lsb(),
69 slice.lsb() + slice.width() - 1,
70 )
71}
72
73fn signal_slice_type(
74 artifact: &FrontendArtifact,
75 slice: SignalSlice,
76) -> Result<ValueType, FrontendArtifactError> {
77 let signal_type = artifact
78 .signal(slice.signal())
79 .ok_or(FrontendArtifactError::UnknownSignal(slice.signal().index()))?
80 .value_type();
81 Ok(ValueType::new(
82 slice.width(),
83 signal_type.is_signed() && slice.width() == signal_type.width(),
84 signal_type.is_four_state(),
85 )?)
86}
87
88fn binary_op(op: celox_frontend_sdk::BinaryOp) -> Result<BinaryOp, FrontendArtifactError> {
89 Ok(match op {
90 celox_frontend_sdk::BinaryOp::Add => BinaryOp::Add,
91 celox_frontend_sdk::BinaryOp::Sub => BinaryOp::Sub,
92 celox_frontend_sdk::BinaryOp::Mul => BinaryOp::Mul,
93 celox_frontend_sdk::BinaryOp::DivUnsigned => BinaryOp::DivU,
94 celox_frontend_sdk::BinaryOp::DivSigned => BinaryOp::DivS,
95 celox_frontend_sdk::BinaryOp::RemUnsigned => BinaryOp::RemU,
96 celox_frontend_sdk::BinaryOp::RemSigned => BinaryOp::RemS,
97 celox_frontend_sdk::BinaryOp::And => BinaryOp::And,
98 celox_frontend_sdk::BinaryOp::Or => BinaryOp::Or,
99 celox_frontend_sdk::BinaryOp::Xor => BinaryOp::Xor,
100 celox_frontend_sdk::BinaryOp::ShiftLeft => BinaryOp::Shl,
101 celox_frontend_sdk::BinaryOp::ShiftRight => BinaryOp::Shr,
102 celox_frontend_sdk::BinaryOp::ArithmeticShiftRight => BinaryOp::Sar,
103 celox_frontend_sdk::BinaryOp::Equal => BinaryOp::Eq,
104 celox_frontend_sdk::BinaryOp::NotEqual => BinaryOp::Ne,
105 celox_frontend_sdk::BinaryOp::CaseEqual => BinaryOp::EqCase,
106 celox_frontend_sdk::BinaryOp::CaseNotEqual => BinaryOp::NeCase,
107 celox_frontend_sdk::BinaryOp::LessUnsigned => BinaryOp::LtU,
108 celox_frontend_sdk::BinaryOp::LessSigned => BinaryOp::LtS,
109 celox_frontend_sdk::BinaryOp::LessEqualUnsigned => BinaryOp::LeU,
110 celox_frontend_sdk::BinaryOp::LessEqualSigned => BinaryOp::LeS,
111 celox_frontend_sdk::BinaryOp::GreaterUnsigned => BinaryOp::GtU,
112 celox_frontend_sdk::BinaryOp::GreaterSigned => BinaryOp::GtS,
113 celox_frontend_sdk::BinaryOp::GreaterEqualUnsigned => BinaryOp::GeU,
114 celox_frontend_sdk::BinaryOp::GreaterEqualSigned => BinaryOp::GeS,
115 celox_frontend_sdk::BinaryOp::LogicAnd => BinaryOp::LogicAnd,
116 celox_frontend_sdk::BinaryOp::LogicOr => BinaryOp::LogicOr,
117 _ => return Err(FrontendArtifactError::UnsupportedOperation),
118 })
119}
120
121fn unary_op(op: celox_frontend_sdk::UnaryOp) -> Result<UnaryOp, FrontendArtifactError> {
122 Ok(match op {
123 celox_frontend_sdk::UnaryOp::ToTwoState => UnaryOp::ToTwoState,
124 celox_frontend_sdk::UnaryOp::Negate => UnaryOp::Minus,
125 celox_frontend_sdk::UnaryOp::BitNot => UnaryOp::BitNot,
126 celox_frontend_sdk::UnaryOp::LogicNot => UnaryOp::LogicNot,
127 celox_frontend_sdk::UnaryOp::ReduceAnd => UnaryOp::And,
128 celox_frontend_sdk::UnaryOp::ReduceOr => UnaryOp::Or,
129 celox_frontend_sdk::UnaryOp::ReduceXor => UnaryOp::Xor,
130 celox_frontend_sdk::UnaryOp::PopCount => UnaryOp::PopCount,
131 celox_frontend_sdk::UnaryOp::CountLeadingZeros => UnaryOp::CountLeadingZeros,
132 celox_frontend_sdk::UnaryOp::CountTrailingZeros => UnaryOp::CountTrailingZeros,
133 _ => return Err(FrontendArtifactError::UnsupportedOperation),
134 })
135}
136
137fn expression_sources(
138 artifact: &FrontendArtifact,
139 id: ExprId,
140 sources: &mut HashSet<VarAtomBase<SourceVarId>>,
141 visited: &mut HashSet<ExprId>,
142) -> Result<(), FrontendArtifactError> {
143 if !visited.insert(id) {
144 return Ok(());
145 }
146 let expression = artifact
147 .expression(id)
148 .ok_or(FrontendArtifactError::UnknownExpression(id.index()))?;
149 match expression.node() {
150 ExprNode::Signal(slice) => {
151 sources.insert(signal_atom(*slice));
152 }
153 ExprNode::Constant(_) => {}
154 ExprNode::Binary { lhs, rhs, .. } => {
155 expression_sources(artifact, *lhs, sources, visited)?;
156 expression_sources(artifact, *rhs, sources, visited)?;
157 }
158 ExprNode::Unary { input, .. } | ExprNode::Slice { input, .. } => {
159 expression_sources(artifact, *input, sources, visited)?;
160 }
161 ExprNode::Mux {
162 condition,
163 then_expr,
164 else_expr,
165 } => {
166 expression_sources(artifact, *condition, sources, visited)?;
167 expression_sources(artifact, *then_expr, sources, visited)?;
168 expression_sources(artifact, *else_expr, sources, visited)?;
169 }
170 ExprNode::Concat(parts) => {
171 for part in parts {
172 expression_sources(artifact, *part, sources, visited)?;
173 }
174 }
175 _ => return Err(FrontendArtifactError::UnsupportedOperation),
176 }
177 Ok(())
178}
179
180fn coerce_slt_expression(
181 artifact: &FrontendArtifact,
182 id: ExprId,
183 target_width: usize,
184 arena: &mut SLTNodeArena<SourceVarId>,
185 cache: &mut HashMap<ExprId, NodeId>,
186) -> Result<NodeId, FrontendArtifactError> {
187 let value_type = artifact
188 .expression(id)
189 .ok_or(FrontendArtifactError::UnknownExpression(id.index()))?
190 .value_type();
191 let node = lower_slt_expression(artifact, id, arena, cache)?;
192 Ok(coerce_node_width(
193 arena,
194 node,
195 Some(target_width),
196 value_type.is_signed(),
197 )?)
198}
199
200fn coerce_slt_expression_to_type(
201 artifact: &FrontendArtifact,
202 id: ExprId,
203 target_type: ValueType,
204 arena: &mut SLTNodeArena<SourceVarId>,
205 cache: &mut HashMap<ExprId, NodeId>,
206) -> Result<NodeId, FrontendArtifactError> {
207 let value_type = artifact
208 .expression(id)
209 .ok_or(FrontendArtifactError::UnknownExpression(id.index()))?
210 .value_type();
211 let node = lower_slt_expression(artifact, id, arena, cache)?;
212 if value_type == target_type {
213 Ok(node)
214 } else {
215 finish_slt_expression(arena, node, target_type)
216 }
217}
218
219fn finish_slt_expression(
220 arena: &mut SLTNodeArena<SourceVarId>,
221 node: NodeId,
222 value_type: ValueType,
223) -> Result<NodeId, FrontendArtifactError> {
224 let node = coerce_node_width(
225 arena,
226 node,
227 Some(value_type.width()),
228 value_type.is_signed(),
229 )?;
230 if value_type.is_four_state() {
231 Ok(node)
232 } else {
233 Ok(arena.alloc(SLTNode::Unary(UnaryOp::ToTwoState, node))?)
234 }
235}
236
237fn lower_slt_expression(
238 artifact: &FrontendArtifact,
239 id: ExprId,
240 arena: &mut SLTNodeArena<SourceVarId>,
241 cache: &mut HashMap<ExprId, NodeId>,
242) -> Result<NodeId, FrontendArtifactError> {
243 if let Some(node) = cache.get(&id) {
244 return Ok(*node);
245 }
246 let expression = artifact
247 .expression(id)
248 .ok_or(FrontendArtifactError::UnknownExpression(id.index()))?;
249 let node = match expression.node() {
250 ExprNode::Signal(slice) => SLTNode::Input {
251 variable: source_id(slice.signal()),
252 signed: expression.value_type().is_signed(),
253 index: Vec::new(),
254 access: BitAccess::new(slice.lsb(), slice.lsb() + slice.width() - 1),
255 },
256 ExprNode::Constant(value) => SLTNode::Constant(
257 value.payload().clone(),
258 value.mask().clone(),
259 value.value_type().width(),
260 value.value_type().is_signed(),
261 ),
262 ExprNode::Binary { op, lhs, rhs } => {
263 use celox_frontend_sdk::BinaryOp as SdkBinaryOp;
264
265 let lhs_type = artifact
266 .expression(*lhs)
267 .ok_or(FrontendArtifactError::UnknownExpression(lhs.index()))?
268 .value_type();
269 let rhs_type = artifact
270 .expression(*rhs)
271 .ok_or(FrontendArtifactError::UnknownExpression(rhs.index()))?
272 .value_type();
273 let (lhs, rhs) = match op {
274 SdkBinaryOp::ShiftLeft
275 | SdkBinaryOp::ShiftRight
276 | SdkBinaryOp::ArithmeticShiftRight => (
277 coerce_slt_expression(
278 artifact,
279 *lhs,
280 expression.value_type().width(),
281 arena,
282 cache,
283 )?,
284 lower_slt_expression(artifact, *rhs, arena, cache)?,
285 ),
286 SdkBinaryOp::Equal
287 | SdkBinaryOp::NotEqual
288 | SdkBinaryOp::CaseEqual
289 | SdkBinaryOp::CaseNotEqual
290 | SdkBinaryOp::LessUnsigned
291 | SdkBinaryOp::LessSigned
292 | SdkBinaryOp::LessEqualUnsigned
293 | SdkBinaryOp::LessEqualSigned
294 | SdkBinaryOp::GreaterUnsigned
295 | SdkBinaryOp::GreaterSigned
296 | SdkBinaryOp::GreaterEqualUnsigned
297 | SdkBinaryOp::GreaterEqualSigned => {
298 let operand_width = lhs_type.width().max(rhs_type.width());
299 (
300 coerce_slt_expression(artifact, *lhs, operand_width, arena, cache)?,
301 coerce_slt_expression(artifact, *rhs, operand_width, arena, cache)?,
302 )
303 }
304 SdkBinaryOp::LogicAnd | SdkBinaryOp::LogicOr => (
305 lower_slt_expression(artifact, *lhs, arena, cache)?,
306 lower_slt_expression(artifact, *rhs, arena, cache)?,
307 ),
308 _ => (
309 coerce_slt_expression(
310 artifact,
311 *lhs,
312 expression.value_type().width(),
313 arena,
314 cache,
315 )?,
316 coerce_slt_expression(
317 artifact,
318 *rhs,
319 expression.value_type().width(),
320 arena,
321 cache,
322 )?,
323 ),
324 };
325 SLTNode::Binary(lhs, binary_op(*op)?, rhs)
326 }
327 ExprNode::Unary { op, input } => {
328 let input = match op {
329 celox_frontend_sdk::UnaryOp::Negate | celox_frontend_sdk::UnaryOp::BitNot => {
330 coerce_slt_expression(
331 artifact,
332 *input,
333 expression.value_type().width(),
334 arena,
335 cache,
336 )?
337 }
338 _ => lower_slt_expression(artifact, *input, arena, cache)?,
339 };
340 SLTNode::Unary(unary_op(*op)?, input)
341 }
342 ExprNode::Mux {
343 condition,
344 then_expr,
345 else_expr,
346 } => SLTNode::Mux {
347 cond: lower_slt_expression(artifact, *condition, arena, cache)?,
348 then_expr: coerce_slt_expression(
349 artifact,
350 *then_expr,
351 expression.value_type().width(),
352 arena,
353 cache,
354 )?,
355 else_expr: coerce_slt_expression(
356 artifact,
357 *else_expr,
358 expression.value_type().width(),
359 arena,
360 cache,
361 )?,
362 },
363 ExprNode::Concat(parts) => SLTNode::Concat(
364 parts
365 .iter()
366 .map(|part| {
367 let expression = artifact
368 .expression(*part)
369 .ok_or(FrontendArtifactError::UnknownExpression(part.index()))?;
370 Ok((
371 lower_slt_expression(artifact, *part, arena, cache)?,
372 expression.value_type().width(),
373 ))
374 })
375 .collect::<Result<Vec<_>, FrontendArtifactError>>()?,
376 ),
377 ExprNode::Slice { input, lsb } => SLTNode::Slice {
378 expr: lower_slt_expression(artifact, *input, arena, cache)?,
379 access: BitAccess::new(*lsb, *lsb + expression.value_type().width() - 1),
380 },
381 _ => return Err(FrontendArtifactError::UnsupportedOperation),
382 };
383 let node = arena.alloc(node)?;
384 let node = finish_slt_expression(arena, node, expression.value_type())?;
385 cache.insert(id, node);
386 Ok(node)
387}
388
389fn alloc_register(
390 builder: &mut SIRBuilder<RegionedSourceAddr>,
391 ty: celox_frontend_sdk::ValueType,
392) -> RegisterId {
393 if ty.is_four_state() {
394 builder.alloc_logic(ty.width())
395 } else {
396 builder.alloc_bit(ty.width(), ty.is_signed())
397 }
398}
399
400fn coerce_sir_register(
401 builder: &mut SIRBuilder<RegionedSourceAddr>,
402 input: RegisterId,
403 input_type: ValueType,
404 target_type: ValueType,
405) -> Result<RegisterId, FrontendArtifactError> {
406 let mut current = input;
407 let mut current_type = input_type;
408
409 if current_type.width() > target_type.width() {
410 let narrowed_type = ValueType::new(
411 target_type.width(),
412 current_type.is_signed(),
413 current_type.is_four_state(),
414 )?;
415 let narrowed = alloc_register(builder, narrowed_type);
416 builder.emit(SIRInstruction::Slice(
417 narrowed,
418 current,
419 0,
420 target_type.width(),
421 ));
422 current = narrowed;
423 current_type = narrowed_type;
424 } else if current_type.width() < target_type.width() {
425 let extension_width = target_type.width() - current_type.width();
426 let extension_type = ValueType::new(extension_width, false, current_type.is_four_state())?;
427 let extension = alloc_register(builder, extension_type);
428 if current_type.is_signed() {
429 let sign_type = ValueType::new(1, false, current_type.is_four_state())?;
430 let sign = alloc_register(builder, sign_type);
431 builder.emit(SIRInstruction::Slice(
432 sign,
433 current,
434 current_type.width() - 1,
435 1,
436 ));
437 builder.emit(SIRInstruction::Concat(
438 extension,
439 vec![sign; extension_width],
440 ));
441 } else {
442 builder.emit(SIRInstruction::Imm(extension, SIRValue::new(0u8)));
443 }
444 let widened_type = ValueType::new(
445 target_type.width(),
446 current_type.is_signed(),
447 current_type.is_four_state(),
448 )?;
449 let widened = alloc_register(builder, widened_type);
450 builder.emit(SIRInstruction::Concat(widened, vec![extension, current]));
451 current = widened;
452 current_type = widened_type;
453 }
454
455 if current_type.is_four_state() && !target_type.is_four_state() {
456 let converted = alloc_register(builder, target_type);
457 builder.emit(SIRInstruction::Unary(
458 converted,
459 UnaryOp::ToTwoState,
460 current,
461 ));
462 return Ok(converted);
463 }
464
465 if current_type.is_four_state() != target_type.is_four_state()
466 || (!target_type.is_four_state() && current_type.is_signed() != target_type.is_signed())
467 {
468 let converted = alloc_register(builder, target_type);
469 builder.emit(SIRInstruction::Unary(converted, UnaryOp::Ident, current));
470 return Ok(converted);
471 }
472
473 Ok(current)
474}
475
476fn coerce_sir_expression(
477 artifact: &FrontendArtifact,
478 id: ExprId,
479 target_type: ValueType,
480 builder: &mut SIRBuilder<RegionedSourceAddr>,
481 cache: &mut HashMap<ExprId, RegisterId>,
482) -> Result<RegisterId, FrontendArtifactError> {
483 let input_type = artifact
484 .expression(id)
485 .ok_or(FrontendArtifactError::UnknownExpression(id.index()))?
486 .value_type();
487 let input = lower_sir_expression(artifact, id, builder, cache)?;
488 coerce_sir_register(builder, input, input_type, target_type)
489}
490
491fn lower_sir_expression(
492 artifact: &FrontendArtifact,
493 id: ExprId,
494 builder: &mut SIRBuilder<RegionedSourceAddr>,
495 cache: &mut HashMap<ExprId, RegisterId>,
496) -> Result<RegisterId, FrontendArtifactError> {
497 if let Some(register) = cache.get(&id) {
498 return Ok(*register);
499 }
500 let expression = artifact
501 .expression(id)
502 .ok_or(FrontendArtifactError::UnknownExpression(id.index()))?;
503 let result = match expression.node() {
504 ExprNode::Signal(slice) => {
505 let result = alloc_register(builder, expression.value_type());
506 builder.emit(SIRInstruction::Load(
507 result,
508 RegionedSourceAddr {
509 region: STABLE_REGION,
510 var_id: source_id(slice.signal()),
511 },
512 SIROffset::Static(slice.lsb()),
513 slice.width(),
514 ));
515 result
516 }
517 ExprNode::Constant(value) => {
518 let result = alloc_register(builder, expression.value_type());
519 builder.emit(SIRInstruction::Imm(
520 result,
521 SIRValue::new_four_state(value.payload().clone(), value.mask().clone()),
522 ));
523 result
524 }
525 ExprNode::Binary { op, lhs, rhs } => {
526 use celox_frontend_sdk::BinaryOp as SdkBinaryOp;
527
528 let lhs_type = artifact
529 .expression(*lhs)
530 .ok_or(FrontendArtifactError::UnknownExpression(lhs.index()))?
531 .value_type();
532 let rhs_type = artifact
533 .expression(*rhs)
534 .ok_or(FrontendArtifactError::UnknownExpression(rhs.index()))?
535 .value_type();
536 let (lhs, rhs) = match op {
537 SdkBinaryOp::ShiftLeft
538 | SdkBinaryOp::ShiftRight
539 | SdkBinaryOp::ArithmeticShiftRight => (
540 coerce_sir_expression(
541 artifact,
542 *lhs,
543 ValueType::new(
544 expression.value_type().width(),
545 lhs_type.is_signed(),
546 lhs_type.is_four_state(),
547 )?,
548 builder,
549 cache,
550 )?,
551 lower_sir_expression(artifact, *rhs, builder, cache)?,
552 ),
553 SdkBinaryOp::Equal
554 | SdkBinaryOp::NotEqual
555 | SdkBinaryOp::CaseEqual
556 | SdkBinaryOp::CaseNotEqual
557 | SdkBinaryOp::LessUnsigned
558 | SdkBinaryOp::LessSigned
559 | SdkBinaryOp::LessEqualUnsigned
560 | SdkBinaryOp::LessEqualSigned
561 | SdkBinaryOp::GreaterUnsigned
562 | SdkBinaryOp::GreaterSigned
563 | SdkBinaryOp::GreaterEqualUnsigned
564 | SdkBinaryOp::GreaterEqualSigned => {
565 let width = lhs_type.width().max(rhs_type.width());
566 (
567 coerce_sir_expression(
568 artifact,
569 *lhs,
570 ValueType::new(width, lhs_type.is_signed(), lhs_type.is_four_state())?,
571 builder,
572 cache,
573 )?,
574 coerce_sir_expression(
575 artifact,
576 *rhs,
577 ValueType::new(width, rhs_type.is_signed(), rhs_type.is_four_state())?,
578 builder,
579 cache,
580 )?,
581 )
582 }
583 SdkBinaryOp::LogicAnd | SdkBinaryOp::LogicOr => (
584 lower_sir_expression(artifact, *lhs, builder, cache)?,
585 lower_sir_expression(artifact, *rhs, builder, cache)?,
586 ),
587 _ => (
588 coerce_sir_expression(
589 artifact,
590 *lhs,
591 ValueType::new(
592 expression.value_type().width(),
593 lhs_type.is_signed(),
594 lhs_type.is_four_state(),
595 )?,
596 builder,
597 cache,
598 )?,
599 coerce_sir_expression(
600 artifact,
601 *rhs,
602 ValueType::new(
603 expression.value_type().width(),
604 rhs_type.is_signed(),
605 rhs_type.is_four_state(),
606 )?,
607 builder,
608 cache,
609 )?,
610 ),
611 };
612 let is_boolean = matches!(
613 op,
614 SdkBinaryOp::Equal
615 | SdkBinaryOp::NotEqual
616 | SdkBinaryOp::CaseEqual
617 | SdkBinaryOp::CaseNotEqual
618 | SdkBinaryOp::LessUnsigned
619 | SdkBinaryOp::LessSigned
620 | SdkBinaryOp::LessEqualUnsigned
621 | SdkBinaryOp::LessEqualSigned
622 | SdkBinaryOp::GreaterUnsigned
623 | SdkBinaryOp::GreaterSigned
624 | SdkBinaryOp::GreaterEqualUnsigned
625 | SdkBinaryOp::GreaterEqualSigned
626 | SdkBinaryOp::LogicAnd
627 | SdkBinaryOp::LogicOr
628 );
629 let case_equality = matches!(op, SdkBinaryOp::CaseEqual | SdkBinaryOp::CaseNotEqual);
630 let operation_four_state = expression.value_type().is_four_state()
631 || (!case_equality && (lhs_type.is_four_state() || rhs_type.is_four_state()));
632 let operation_type = ValueType::new(
633 if is_boolean {
634 1
635 } else {
636 expression.value_type().width()
637 },
638 !is_boolean && expression.value_type().is_signed(),
639 operation_four_state,
640 )?;
641 let operation_result = alloc_register(builder, operation_type);
642 builder.emit(SIRInstruction::Binary(
643 operation_result,
644 lhs,
645 binary_op(*op)?,
646 rhs,
647 ));
648 coerce_sir_register(
649 builder,
650 operation_result,
651 operation_type,
652 expression.value_type(),
653 )?
654 }
655 ExprNode::Unary { op, input } => {
656 use celox_frontend_sdk::UnaryOp as SdkUnaryOp;
657
658 let input_type = artifact
659 .expression(*input)
660 .ok_or(FrontendArtifactError::UnknownExpression(input.index()))?
661 .value_type();
662 let (input, operation_type) = match op {
663 SdkUnaryOp::Negate | SdkUnaryOp::BitNot => (
664 coerce_sir_expression(
665 artifact,
666 *input,
667 ValueType::new(
668 expression.value_type().width(),
669 input_type.is_signed(),
670 input_type.is_four_state(),
671 )?,
672 builder,
673 cache,
674 )?,
675 ValueType::new(
676 expression.value_type().width(),
677 expression.value_type().is_signed(),
678 expression.value_type().is_four_state() || input_type.is_four_state(),
679 )?,
680 ),
681 SdkUnaryOp::LogicNot
682 | SdkUnaryOp::ReduceAnd
683 | SdkUnaryOp::ReduceOr
684 | SdkUnaryOp::ReduceXor => (
685 lower_sir_expression(artifact, *input, builder, cache)?,
686 ValueType::new(
687 1,
688 false,
689 expression.value_type().is_four_state() || input_type.is_four_state(),
690 )?,
691 ),
692 SdkUnaryOp::ToTwoState => (
693 lower_sir_expression(artifact, *input, builder, cache)?,
694 ValueType::new(input_type.width(), input_type.is_signed(), false)?,
695 ),
696 SdkUnaryOp::PopCount
697 | SdkUnaryOp::CountLeadingZeros
698 | SdkUnaryOp::CountTrailingZeros => (
699 lower_sir_expression(artifact, *input, builder, cache)?,
700 ValueType::new(
701 unary_op(*op)?.result_width(input_type.width()),
702 false,
703 expression.value_type().is_four_state() || input_type.is_four_state(),
704 )?,
705 ),
706 _ => return Err(FrontendArtifactError::UnsupportedOperation),
707 };
708 let operation_result = alloc_register(builder, operation_type);
709 builder.emit(SIRInstruction::Unary(
710 operation_result,
711 unary_op(*op)?,
712 input,
713 ));
714 coerce_sir_register(
715 builder,
716 operation_result,
717 operation_type,
718 expression.value_type(),
719 )?
720 }
721 ExprNode::Mux {
722 condition,
723 then_expr,
724 else_expr,
725 } => {
726 let condition_type = artifact
727 .expression(*condition)
728 .ok_or(FrontendArtifactError::UnknownExpression(condition.index()))?
729 .value_type();
730 let condition = lower_sir_expression(artifact, *condition, builder, cache)?;
731 let then_type = artifact
732 .expression(*then_expr)
733 .ok_or(FrontendArtifactError::UnknownExpression(then_expr.index()))?
734 .value_type();
735 let else_type = artifact
736 .expression(*else_expr)
737 .ok_or(FrontendArtifactError::UnknownExpression(else_expr.index()))?
738 .value_type();
739 let then_expr = coerce_sir_expression(
740 artifact,
741 *then_expr,
742 ValueType::new(
743 expression.value_type().width(),
744 then_type.is_signed(),
745 then_type.is_four_state(),
746 )?,
747 builder,
748 cache,
749 )?;
750 let else_expr = coerce_sir_expression(
751 artifact,
752 *else_expr,
753 ValueType::new(
754 expression.value_type().width(),
755 else_type.is_signed(),
756 else_type.is_four_state(),
757 )?,
758 builder,
759 cache,
760 )?;
761 let operation_type = ValueType::new(
762 expression.value_type().width(),
763 expression.value_type().is_signed(),
764 expression.value_type().is_four_state()
765 || condition_type.is_four_state()
766 || then_type.is_four_state()
767 || else_type.is_four_state(),
768 )?;
769 let operation_result = alloc_register(builder, operation_type);
770 builder.emit(SIRInstruction::Mux(
771 operation_result,
772 condition,
773 then_expr,
774 else_expr,
775 ));
776 coerce_sir_register(
777 builder,
778 operation_result,
779 operation_type,
780 expression.value_type(),
781 )?
782 }
783 ExprNode::Concat(parts) => {
784 let parts = parts
785 .iter()
786 .map(|part| lower_sir_expression(artifact, *part, builder, cache))
787 .collect::<Result<Vec<_>, _>>()?;
788 let result = alloc_register(builder, expression.value_type());
789 builder.emit(SIRInstruction::Concat(result, parts));
790 result
791 }
792 ExprNode::Slice { input, lsb } => {
793 let input = lower_sir_expression(artifact, *input, builder, cache)?;
794 let result = alloc_register(builder, expression.value_type());
795 builder.emit(SIRInstruction::Slice(
796 result,
797 input,
798 *lsb,
799 expression.value_type().width(),
800 ));
801 result
802 }
803 _ => return Err(FrontendArtifactError::UnsupportedOperation),
804 };
805 cache.insert(id, result);
806 Ok(result)
807}
808
809fn lower_control(
810 artifact: &FrontendArtifact,
811 signal: SignalId,
812 active: ActiveLevel,
813 builder: &mut SIRBuilder<RegionedSourceAddr>,
814) -> Result<RegisterId, FrontendArtifactError> {
815 let signal_info = artifact
816 .signal(signal)
817 .ok_or(FrontendArtifactError::UnknownSignal(signal.index()))?;
818 let loaded = alloc_register(builder, signal_info.value_type());
819 builder.emit(SIRInstruction::Load(
820 loaded,
821 RegionedSourceAddr {
822 region: STABLE_REGION,
823 var_id: source_id(signal),
824 },
825 SIROffset::Static(0),
826 1,
827 ));
828 let polarized = if active == ActiveLevel::Low {
829 let inverted = alloc_register(
830 builder,
831 ValueType::new(1, false, signal_info.value_type().is_four_state())?,
832 );
833 builder.emit(SIRInstruction::Unary(inverted, UnaryOp::LogicNot, loaded));
834 inverted
835 } else {
836 loaded
837 };
838 if signal_info.value_type().is_four_state() {
839 let result = builder.alloc_bit(1, false);
840 builder.emit(SIRInstruction::Unary(
841 result,
842 UnaryOp::ToTwoState,
843 polarized,
844 ));
845 Ok(result)
846 } else {
847 Ok(polarized)
848 }
849}
850
851fn seal_builder(mut builder: SIRBuilder<RegionedSourceAddr>) -> ExecutionUnit<RegionedSourceAddr> {
852 builder.seal_block(SIRTerminator::Return);
853 let (blocks, register_map, _) = builder.drain();
854 ExecutionUnit {
855 entry_block_id: BlockId(0),
856 blocks,
857 register_map,
858 }
859}
860
861fn insert_or_merge(
862 blocks: &mut HashMap<TriggerSet<SourceVarId>, ExecutionUnit<RegionedSourceAddr>>,
863 trigger: TriggerSet<SourceVarId>,
864 unit: ExecutionUnit<RegionedSourceAddr>,
865) {
866 if let Some(existing) = blocks.remove(&trigger) {
867 blocks.insert(trigger, merge_sir_eus(&[existing, unit]).0);
868 } else {
869 blocks.insert(trigger, unit);
870 }
871}
872
873fn lower_registers(
874 artifact: &FrontendArtifact,
875 eval_only: &mut HashMap<TriggerSet<SourceVarId>, ExecutionUnit<RegionedSourceAddr>>,
876 apply: &mut HashMap<TriggerSet<SourceVarId>, ExecutionUnit<RegionedSourceAddr>>,
877 eval_apply: &mut HashMap<TriggerSet<SourceVarId>, ExecutionUnit<RegionedSourceAddr>>,
878 reset_clock_map: &mut HashMap<SourceVarId, SourceVarId>,
879) -> Result<(), FrontendArtifactError> {
880 for register in artifact.registers() {
881 let target = register.target();
882 let target_id = source_id(target.signal());
883 let trigger = TriggerSet {
884 clock: source_id(register.clock()),
885 resets: register
886 .async_reset()
887 .into_iter()
888 .map(|reset| source_id(reset.signal()))
889 .collect(),
890 };
891 if let Some(reset) = register.async_reset() {
892 let reset_id = source_id(reset.signal());
893 let clock_id = source_id(register.clock());
894 if let Some(first_clock_id) = reset_clock_map.get(&reset_id)
895 && *first_clock_id != clock_id
896 {
897 let signal_name = |id: SignalId| {
898 artifact
899 .signal(id)
900 .map(|signal| signal.name().to_string())
901 .ok_or(FrontendArtifactError::UnknownSignal(id.index()))
902 };
903 let first_clock = artifact
904 .signals()
905 .get(first_clock_id.0 as usize)
906 .ok_or(FrontendArtifactError::UnknownSignal(first_clock_id.0))?;
907 return Err(FrontendArtifactError::SharedResetAcrossClocks {
908 reset: signal_name(reset.signal())?,
909 first_clock: first_clock.name().to_string(),
910 second_clock: signal_name(register.clock())?,
911 });
912 }
913 reset_clock_map.insert(reset_id, clock_id);
914 }
915
916 let build_eval =
917 |commit: bool| -> Result<ExecutionUnit<RegionedSourceAddr>, FrontendArtifactError> {
918 let mut builder = SIRBuilder::new();
919 let target_info = artifact.signal(target.signal()).ok_or(
920 FrontendArtifactError::UnknownSignal(target.signal().index()),
921 )?;
922 let target_type = target_info.value_type();
923 builder.emit(SIRInstruction::Commit(
924 RegionedSourceAddr {
925 region: STABLE_REGION,
926 var_id: target_id,
927 },
928 RegionedSourceAddr {
929 region: WORKING_REGION,
930 var_id: target_id,
931 },
932 SIROffset::Static(0),
933 target.width(),
934 Vec::new(),
935 ));
936 let mut cache = HashMap::default();
937 let mut next = coerce_sir_expression(
938 artifact,
939 register.next(),
940 target_type,
941 &mut builder,
942 &mut cache,
943 )?;
944 if let Some(enable) = register.enable() {
945 let condition =
946 lower_control(artifact, enable.signal(), enable.active(), &mut builder)?;
947 let current = alloc_register(&mut builder, target_type);
948 builder.emit(SIRInstruction::Load(
949 current,
950 RegionedSourceAddr {
951 region: STABLE_REGION,
952 var_id: target_id,
953 },
954 SIROffset::Static(0),
955 target.width(),
956 ));
957 let selected = alloc_register(&mut builder, target_type);
958 builder.emit(SIRInstruction::Mux(selected, condition, next, current));
959 next = selected;
960 }
961 if let Some(reset) = register.async_reset() {
962 let condition =
963 lower_control(artifact, reset.signal(), reset.active(), &mut builder)?;
964 let reset_value = coerce_sir_expression(
965 artifact,
966 reset.value(),
967 target_type,
968 &mut builder,
969 &mut cache,
970 )?;
971 let selected = alloc_register(&mut builder, target_type);
972 builder.emit(SIRInstruction::Mux(selected, condition, reset_value, next));
973 next = selected;
974 }
975 builder.emit(SIRInstruction::Store(
976 RegionedSourceAddr {
977 region: WORKING_REGION,
978 var_id: target_id,
979 },
980 SIROffset::Static(0),
981 target.width(),
982 next,
983 Vec::new(),
984 Vec::new(),
985 ));
986 if commit {
987 builder.emit(SIRInstruction::Commit(
988 RegionedSourceAddr {
989 region: WORKING_REGION,
990 var_id: target_id,
991 },
992 RegionedSourceAddr {
993 region: STABLE_REGION,
994 var_id: target_id,
995 },
996 SIROffset::Static(0),
997 target.width(),
998 Vec::new(),
999 ));
1000 }
1001 Ok(seal_builder(builder))
1002 };
1003
1004 let mut apply_builder = SIRBuilder::new();
1005 apply_builder.emit(SIRInstruction::Commit(
1006 RegionedSourceAddr {
1007 region: WORKING_REGION,
1008 var_id: target_id,
1009 },
1010 RegionedSourceAddr {
1011 region: STABLE_REGION,
1012 var_id: target_id,
1013 },
1014 SIROffset::Static(0),
1015 target.width(),
1016 Vec::new(),
1017 ));
1018 insert_or_merge(eval_only, trigger.clone(), build_eval(false)?);
1019 insert_or_merge(apply, trigger.clone(), seal_builder(apply_builder));
1020 insert_or_merge(eval_apply, trigger, build_eval(true)?);
1021 }
1022 Ok(())
1023}
1024
1025fn set_role(
1026 roles: &mut HashMap<SignalId, (DomainKind, PortTypeKind)>,
1027 artifact: &FrontendArtifact,
1028 signal: SignalId,
1029 role: (DomainKind, PortTypeKind),
1030) -> Result<(), FrontendArtifactError> {
1031 if let Some(existing) = roles.get(&signal) {
1032 if *existing != role {
1033 let signal = artifact
1034 .signal(signal)
1035 .ok_or(FrontendArtifactError::UnknownSignal(signal.index()))?;
1036 return Err(FrontendArtifactError::ConflictingSignalRole {
1037 signal: signal.name().to_string(),
1038 });
1039 }
1040 } else {
1041 roles.insert(signal, role);
1042 }
1043 Ok(())
1044}
1045
1046pub fn lower_frontend_artifact(
1048 artifact: &FrontendArtifact,
1049) -> Result<LoweredFrontendArtifact, FrontendArtifactError> {
1050 artifact.validate()?;
1051 let mut roles = HashMap::default();
1052 for register in artifact.registers() {
1053 set_role(
1054 &mut roles,
1055 artifact,
1056 register.clock(),
1057 match register.edge() {
1058 Edge::Posedge => (DomainKind::ClockPosedge, PortTypeKind::Clock),
1059 Edge::Negedge => (DomainKind::ClockNegedge, PortTypeKind::Clock),
1060 },
1061 )?;
1062 if let Some(reset) = register.async_reset() {
1063 set_role(
1064 &mut roles,
1065 artifact,
1066 reset.signal(),
1067 match reset.active() {
1068 ActiveLevel::High => (DomainKind::ResetAsyncHigh, PortTypeKind::ResetAsyncHigh),
1069 ActiveLevel::Low => (DomainKind::ResetAsyncLow, PortTypeKind::ResetAsyncLow),
1070 },
1071 )?;
1072 }
1073 }
1074
1075 let variables = artifact
1076 .signals()
1077 .iter()
1078 .map(|signal| {
1079 let (kind, type_kind) = roles.get(&signal.id()).copied().unwrap_or((
1080 DomainKind::Other,
1081 if signal.value_type().is_four_state() {
1082 PortTypeKind::Logic
1083 } else {
1084 PortTypeKind::Bit
1085 },
1086 ));
1087 let variable_kind = match signal.direction() {
1088 Direction::Input => VariableKind::Input,
1089 Direction::Output => VariableKind::Output,
1090 Direction::Inout => VariableKind::Inout,
1091 Direction::Internal => VariableKind::Variable,
1092 _ => VariableKind::Variable,
1093 };
1094 (
1095 source_id(signal.id()),
1096 SymbolicVariable {
1097 path: vec![signal.name().to_string()],
1098 kind: variable_kind,
1099 signed: signal.value_type().is_signed(),
1100 metadata: VariableMetadata {
1101 width: signal.value_type().width(),
1102 is_4state: signal.value_type().is_four_state(),
1103 kind,
1104 type_kind,
1105 array_dims: Vec::new(),
1106 },
1107 packed_dims: vec![signal.value_type().width()],
1108 source: None,
1109 module_affiliated: true,
1110 },
1111 )
1112 })
1113 .collect();
1114
1115 let mut arena = SLTNodeArena::new();
1116 let mut node_cache = HashMap::default();
1117 let mut comb_blocks = Vec::new();
1118 for assignment in artifact.assignments() {
1119 let target_type = signal_slice_type(artifact, assignment.target())?;
1120 let mut sources = HashSet::default();
1121 let mut visited = HashSet::default();
1122 expression_sources(artifact, assignment.value(), &mut sources, &mut visited)?;
1123 comb_blocks.push(LogicPath {
1124 target: LogicPathTarget::Var(signal_atom(assignment.target())),
1125 sources,
1126 previous_sources: HashSet::default(),
1127 address_sources: HashSet::default(),
1128 local_inputs: Vec::new(),
1129 order_before: HashSet::default(),
1130 comb_capture_enable_sites: Vec::new(),
1131 comb_capture_enable_always: false,
1132 pre_lower_nodes: Vec::new(),
1133 expr: coerce_slt_expression_to_type(
1134 artifact,
1135 assignment.value(),
1136 target_type,
1137 &mut arena,
1138 &mut node_cache,
1139 )?,
1140 });
1141 }
1142
1143 let mut eval_only_ff_blocks = HashMap::default();
1144 let mut apply_ff_blocks = HashMap::default();
1145 let mut eval_apply_ff_blocks = HashMap::default();
1146 let mut reset_clock_map = HashMap::default();
1147 lower_registers(
1148 artifact,
1149 &mut eval_only_ff_blocks,
1150 &mut apply_ff_blocks,
1151 &mut eval_apply_ff_blocks,
1152 &mut reset_clock_map,
1153 )?;
1154
1155 let initial_memory_values = artifact
1156 .signals()
1157 .iter()
1158 .filter_map(|signal| {
1159 signal.initial().map(|initial| InitialStateValue {
1160 address: source_id(signal.id()),
1161 data: InitialStateData::Packed {
1162 value: initial.payload().clone(),
1163 mask: initial.mask().clone(),
1164 written_mask: (num_bigint::BigUint::from(1u8) << signal.value_type().width())
1165 - num_bigint::BigUint::from(1u8),
1166 },
1167 })
1168 })
1169 .collect();
1170
1171 let module_id = ModuleId(0);
1172 let sim_module = SimModule {
1173 name: artifact.module_name().to_string(),
1174 variables,
1175 ff_access_summaries: HashMap::default(),
1176 eval_only_ff_blocks,
1177 apply_ff_blocks,
1178 eval_apply_ff_blocks,
1179 glue_blocks: HashMap::default(),
1180 indexed_instance_names: HashSet::default(),
1181 comb_blocks,
1182 comb_observers: Vec::new(),
1183 runtime_errors: HashMap::default(),
1184 runtime_event_sites: Vec::new(),
1185 initial_memory_values,
1186 comb_boundaries: HashMap::default(),
1187 arena,
1188 reset_clock_map,
1189 };
1190 let symbolic = SymbolicRtl {
1191 modules: [(module_id, sim_module.clone())].into_iter().collect(),
1192 module_names: [(module_id, artifact.module_name().to_string())]
1193 .into_iter()
1194 .collect(),
1195 root_id: module_id,
1196 };
1197 let external = ExternalHierarchy {
1198 modules: [(
1199 module_id,
1200 ExternalModule {
1201 sim_module,
1202 port_order: artifact
1203 .port_order()
1204 .iter()
1205 .map(|signal| source_id(*signal))
1206 .collect(),
1207 unresolved_instances: Vec::new(),
1208 },
1209 )]
1210 .into_iter()
1211 .collect(),
1212 roots: [(artifact.module_name().to_string(), module_id)]
1213 .into_iter()
1214 .collect(),
1215 };
1216 Ok(LoweredFrontendArtifact { symbolic, external })
1217}