Skip to main content

oxilean_codegen/chisel_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::{HashMap, HashSet, VecDeque};
6
7#[allow(dead_code)]
8#[derive(Debug, Clone)]
9pub struct ChiselCacheEntry {
10    pub key: String,
11    pub data: Vec<u8>,
12    pub timestamp: u64,
13    pub valid: bool,
14}
15/// Standard interface templates (SRAM, APB, AHB, AXI4-Lite stubs).
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub enum ChiselInterfaceTemplate {
19    SramPort { addr_bits: u32, data_bits: u32 },
20    ApbPort { addr_bits: u32, data_bits: u32 },
21    AhbLitePort { addr_bits: u32, data_bits: u32 },
22    Axi4LitePort { addr_bits: u32, data_bits: u32 },
23}
24#[allow(dead_code)]
25impl ChiselInterfaceTemplate {
26    /// Emit the IO bundle fields for this interface.
27    pub fn emit_ports(&self, prefix: &str, is_master: bool) -> String {
28        match self {
29            ChiselInterfaceTemplate::SramPort {
30                addr_bits,
31                data_bits,
32            } => {
33                let a_dir = if is_master { "Output" } else { "Input" };
34                let d_out_dir = if is_master { "Output" } else { "Input" };
35                let d_in_dir = if is_master { "Input" } else { "Output" };
36                format!(
37                    "    val {p}_addr  = {a}(UInt({ab}.W))\n\
38                     val {p}_wen   = {a}(Bool())\n\
39                     val {p}_wdata = {do}(UInt({db}.W))\n\
40                     val {p}_rdata = {di}(UInt({db}.W))\n\
41                     val {p}_cs    = {a}(Bool())\n",
42                    p = prefix, a = a_dir, do = d_out_dir, di = d_in_dir, ab = addr_bits,
43                    db = data_bits
44                )
45            }
46            ChiselInterfaceTemplate::ApbPort {
47                addr_bits,
48                data_bits,
49            } => {
50                let m_dir = if is_master { "Output" } else { "Input" };
51                let s_dir = if is_master { "Input" } else { "Output" };
52                format!(
53                    "    val {p}_paddr  = {m}(UInt({ab}.W))\n\
54                     val {p}_psel   = {m}(Bool())\n\
55                     val {p}_penable= {m}(Bool())\n\
56                     val {p}_pwrite = {m}(Bool())\n\
57                     val {p}_pwdata = {m}(UInt({db}.W))\n\
58                     val {p}_prdata = {s}(UInt({db}.W))\n\
59                     val {p}_pready = {s}(Bool())\n\
60                     val {p}_pslverr= {s}(Bool())\n",
61                    p = prefix,
62                    m = m_dir,
63                    s = s_dir,
64                    ab = addr_bits,
65                    db = data_bits
66                )
67            }
68            ChiselInterfaceTemplate::AhbLitePort {
69                addr_bits,
70                data_bits,
71            } => {
72                format!(
73                    "    /* AHB-Lite {p} {ab}b addr {db}b data */\n\
74                     val {p}_haddr  = Output(UInt({ab}.W))\n\
75                     val {p}_htrans = Output(UInt(2.W))\n\
76                     val {p}_hwrite = Output(Bool())\n\
77                     val {p}_hwdata = Output(UInt({db}.W))\n\
78                     val {p}_hrdata = Input(UInt({db}.W))\n\
79                     val {p}_hready = Input(Bool())\n\
80                     val {p}_hresp  = Input(Bool())\n",
81                    p = prefix,
82                    ab = addr_bits,
83                    db = data_bits
84                )
85            }
86            ChiselInterfaceTemplate::Axi4LitePort {
87                addr_bits,
88                data_bits,
89            } => {
90                format!(
91                    "    /* AXI4-Lite {p} {ab}b addr {db}b data */\n\
92                     val {p}_awvalid = Output(Bool())\n\
93                     val {p}_awready = Input(Bool())\n\
94                     val {p}_awaddr  = Output(UInt({ab}.W))\n\
95                     val {p}_wvalid  = Output(Bool())\n\
96                     val {p}_wready  = Input(Bool())\n\
97                     val {p}_wdata   = Output(UInt({db}.W))\n\
98                     val {p}_wstrb   = Output(UInt({ws}.W))\n\
99                     val {p}_bvalid  = Input(Bool())\n\
100                     val {p}_bready  = Output(Bool())\n\
101                     val {p}_bresp   = Input(UInt(2.W))\n\
102                     val {p}_arvalid = Output(Bool())\n\
103                     val {p}_arready = Input(Bool())\n\
104                     val {p}_araddr  = Output(UInt({ab}.W))\n\
105                     val {p}_rvalid  = Input(Bool())\n\
106                     val {p}_rready  = Output(Bool())\n\
107                     val {p}_rdata   = Input(UInt({db}.W))\n\
108                     val {p}_rresp   = Input(UInt(2.W))\n",
109                    p = prefix,
110                    ab = addr_bits,
111                    db = data_bits,
112                    ws = data_bits / 8
113                )
114            }
115        }
116    }
117}
118#[allow(dead_code)]
119#[derive(Debug, Clone)]
120pub struct ChiselDepGraph {
121    pub(super) nodes: Vec<u32>,
122    pub(super) edges: Vec<(u32, u32)>,
123}
124impl ChiselDepGraph {
125    #[allow(dead_code)]
126    pub fn new() -> Self {
127        ChiselDepGraph {
128            nodes: Vec::new(),
129            edges: Vec::new(),
130        }
131    }
132    #[allow(dead_code)]
133    pub fn add_node(&mut self, id: u32) {
134        if !self.nodes.contains(&id) {
135            self.nodes.push(id);
136        }
137    }
138    #[allow(dead_code)]
139    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
140        self.add_node(dep);
141        self.add_node(dependent);
142        self.edges.push((dep, dependent));
143    }
144    #[allow(dead_code)]
145    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
146        self.edges
147            .iter()
148            .filter(|(d, _)| *d == node)
149            .map(|(_, dep)| *dep)
150            .collect()
151    }
152    #[allow(dead_code)]
153    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
154        self.edges
155            .iter()
156            .filter(|(_, dep)| *dep == node)
157            .map(|(d, _)| *d)
158            .collect()
159    }
160    #[allow(dead_code)]
161    pub fn topological_sort(&self) -> Vec<u32> {
162        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
163        for &n in &self.nodes {
164            in_degree.insert(n, 0);
165        }
166        for (_, dep) in &self.edges {
167            *in_degree.entry(*dep).or_insert(0) += 1;
168        }
169        let mut queue: std::collections::VecDeque<u32> = self
170            .nodes
171            .iter()
172            .filter(|&&n| in_degree[&n] == 0)
173            .copied()
174            .collect();
175        let mut result = Vec::new();
176        while let Some(node) = queue.pop_front() {
177            result.push(node);
178            for dep in self.dependents_of(node) {
179                let cnt = in_degree.entry(dep).or_insert(0);
180                *cnt = cnt.saturating_sub(1);
181                if *cnt == 0 {
182                    queue.push_back(dep);
183                }
184            }
185        }
186        result
187    }
188    #[allow(dead_code)]
189    pub fn has_cycle(&self) -> bool {
190        self.topological_sort().len() < self.nodes.len()
191    }
192}
193/// SRAM wrapper descriptor.
194#[allow(dead_code)]
195#[derive(Debug, Clone)]
196pub struct ChiselSRAMWrapper {
197    pub name: String,
198    pub depth: u32,
199    pub data_width: u32,
200    pub port_type: SramPortType,
201    pub has_mask: bool,
202    pub mask_granularity: u32,
203    pub use_sync_read: bool,
204    pub pipeline_read: bool,
205}
206#[allow(dead_code)]
207impl ChiselSRAMWrapper {
208    pub fn single_port(name: impl Into<String>, depth: u32, data_width: u32) -> Self {
209        Self {
210            name: name.into(),
211            depth,
212            data_width,
213            port_type: SramPortType::SinglePort,
214            has_mask: false,
215            mask_granularity: 8,
216            use_sync_read: true,
217            pipeline_read: false,
218        }
219    }
220    pub fn simple_dual_port(name: impl Into<String>, depth: u32, data_width: u32) -> Self {
221        let mut s = Self::single_port(name, depth, data_width);
222        s.port_type = SramPortType::SimpleDualPort;
223        s
224    }
225    pub fn true_dual_port(name: impl Into<String>, depth: u32, data_width: u32) -> Self {
226        let mut s = Self::single_port(name, depth, data_width);
227        s.port_type = SramPortType::TrueDualPort;
228        s
229    }
230    pub fn with_mask(mut self, granularity: u32) -> Self {
231        self.has_mask = true;
232        self.mask_granularity = granularity;
233        self
234    }
235    pub fn with_pipeline_read(mut self) -> Self {
236        self.pipeline_read = true;
237        self
238    }
239    pub fn addr_width(&self) -> u32 {
240        if self.depth == 0 {
241            return 1;
242        }
243        (self.depth as f64).log2().ceil() as u32
244    }
245    pub fn mask_width(&self) -> u32 {
246        if !self.has_mask {
247            return 0;
248        }
249        self.data_width / self.mask_granularity
250    }
251    /// Emit the Chisel SRAMInterface module class.
252    pub fn emit(&self) -> String {
253        let aw = self.addr_width();
254        let dw = self.data_width;
255        let mw = self.mask_width();
256        let mut out = format!("class {} extends Module {{\n", self.name);
257        out.push_str("  val io = IO(new Bundle {\n");
258        out.push_str(&format!("    val waddr = Input(UInt({}.W))\n", aw));
259        out.push_str(&format!("    val wdata = Input(UInt({}.W))\n", dw));
260        out.push_str("    val wen   = Input(Bool())\n");
261        if self.has_mask {
262            out.push_str(&format!("    val wmask = Input(UInt({}.W))\n", mw));
263        }
264        match self.port_type {
265            SramPortType::SimpleDualPort | SramPortType::TrueDualPort => {
266                out.push_str(&format!("    val raddr = Input(UInt({}.W))\n", aw));
267                out.push_str("    val ren   = Input(Bool())\n");
268                out.push_str(&format!("    val rdata = Output(UInt({}.W))\n", dw));
269            }
270            SramPortType::SinglePort => {
271                out.push_str(&format!("    val raddr = Input(UInt({}.W))\n", aw));
272                out.push_str(&format!("    val rdata = Output(UInt({}.W))\n", dw));
273            }
274        }
275        out.push_str("  })\n\n");
276        out.push_str(&format!(
277            "  val mem = SyncReadMem({}, UInt({}.W))\n\n",
278            self.depth, dw
279        ));
280        out.push_str("  when (io.wen) {\n");
281        if self.has_mask {
282            out.push_str("    mem.write(io.waddr, io.wdata, io.wmask.asBools)\n");
283        } else {
284            out.push_str("    mem.write(io.waddr, io.wdata)\n");
285        }
286        out.push_str("  }\n\n");
287        if self.pipeline_read {
288            out.push_str("  val raddr_r = RegNext(io.raddr)\n");
289            out.push_str("  io.rdata := mem.read(raddr_r)\n");
290        } else {
291            out.push_str("  io.rdata := mem.read(io.raddr)\n");
292        }
293        out.push_str("}\n");
294        out
295    }
296}
297#[allow(dead_code)]
298#[derive(Debug, Clone)]
299pub struct ChiselAnalysisCache {
300    pub(super) entries: std::collections::HashMap<String, ChiselCacheEntry>,
301    pub(super) max_size: usize,
302    pub(super) hits: u64,
303    pub(super) misses: u64,
304}
305impl ChiselAnalysisCache {
306    #[allow(dead_code)]
307    pub fn new(max_size: usize) -> Self {
308        ChiselAnalysisCache {
309            entries: std::collections::HashMap::new(),
310            max_size,
311            hits: 0,
312            misses: 0,
313        }
314    }
315    #[allow(dead_code)]
316    pub fn get(&mut self, key: &str) -> Option<&ChiselCacheEntry> {
317        if self.entries.contains_key(key) {
318            self.hits += 1;
319            self.entries.get(key)
320        } else {
321            self.misses += 1;
322            None
323        }
324    }
325    #[allow(dead_code)]
326    pub fn insert(&mut self, key: String, data: Vec<u8>) {
327        if self.entries.len() >= self.max_size {
328            if let Some(oldest) = self.entries.keys().next().cloned() {
329                self.entries.remove(&oldest);
330            }
331        }
332        self.entries.insert(
333            key.clone(),
334            ChiselCacheEntry {
335                key,
336                data,
337                timestamp: 0,
338                valid: true,
339            },
340        );
341    }
342    #[allow(dead_code)]
343    pub fn invalidate(&mut self, key: &str) {
344        if let Some(entry) = self.entries.get_mut(key) {
345            entry.valid = false;
346        }
347    }
348    #[allow(dead_code)]
349    pub fn clear(&mut self) {
350        self.entries.clear();
351    }
352    #[allow(dead_code)]
353    pub fn hit_rate(&self) -> f64 {
354        let total = self.hits + self.misses;
355        if total == 0 {
356            return 0.0;
357        }
358        self.hits as f64 / total as f64
359    }
360    #[allow(dead_code)]
361    pub fn size(&self) -> usize {
362        self.entries.len()
363    }
364}
365#[allow(dead_code)]
366pub struct ChiselPassRegistry {
367    pub(super) configs: Vec<ChiselPassConfig>,
368    pub(super) stats: std::collections::HashMap<String, ChiselPassStats>,
369}
370impl ChiselPassRegistry {
371    #[allow(dead_code)]
372    pub fn new() -> Self {
373        ChiselPassRegistry {
374            configs: Vec::new(),
375            stats: std::collections::HashMap::new(),
376        }
377    }
378    #[allow(dead_code)]
379    pub fn register(&mut self, config: ChiselPassConfig) {
380        self.stats
381            .insert(config.pass_name.clone(), ChiselPassStats::new());
382        self.configs.push(config);
383    }
384    #[allow(dead_code)]
385    pub fn enabled_passes(&self) -> Vec<&ChiselPassConfig> {
386        self.configs.iter().filter(|c| c.enabled).collect()
387    }
388    #[allow(dead_code)]
389    pub fn get_stats(&self, name: &str) -> Option<&ChiselPassStats> {
390        self.stats.get(name)
391    }
392    #[allow(dead_code)]
393    pub fn total_passes(&self) -> usize {
394        self.configs.len()
395    }
396    #[allow(dead_code)]
397    pub fn enabled_count(&self) -> usize {
398        self.enabled_passes().len()
399    }
400    #[allow(dead_code)]
401    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
402        if let Some(stats) = self.stats.get_mut(name) {
403            stats.record_run(changes, time_ms, iter);
404        }
405    }
406}
407/// FIRRTL / Chisel annotation kinds.
408#[allow(dead_code)]
409#[derive(Debug, Clone, PartialEq)]
410pub enum ChiselAnnotationKind {
411    DontTouch,
412    ForceNameAnnotation,
413    SynthBlackBox,
414    InlineInstance,
415    NoDedupAnnotation,
416    LoadMemoryAnnotation { file: String },
417}
418#[allow(dead_code)]
419impl ChiselAnnotationKind {
420    pub fn scala_annotation(&self, target: &str) -> String {
421        match self {
422            ChiselAnnotationKind::DontTouch => {
423                format!(
424                    "annotate(new ChiselAnnotation {{ def toFirrtl = DontTouchAnnotation({})}}\n)",
425                    target
426                )
427            }
428            ChiselAnnotationKind::ForceNameAnnotation => {
429                format!(
430                    "annotate(new ChiselAnnotation {{ def toFirrtl = ForcedName(\"{}\", {})}}\n)",
431                    target, target
432                )
433            }
434            ChiselAnnotationKind::SynthBlackBox => {
435                format!("// synthesis black box: {}\n", target)
436            }
437            ChiselAnnotationKind::InlineInstance => {
438                format!(
439                    "annotate(new ChiselAnnotation {{ def toFirrtl = InlineAnnotation({})}}\n)",
440                    target
441                )
442            }
443            ChiselAnnotationKind::NoDedupAnnotation => {
444                format!(
445                    "annotate(new ChiselAnnotation {{ def toFirrtl = NoDedupAnnotation({})}}\n)",
446                    target
447                )
448            }
449            ChiselAnnotationKind::LoadMemoryAnnotation { file } => {
450                format!("loadMemoryFromFile({}, {:?})\n", target, file)
451            }
452        }
453    }
454}
455/// Chisel hardware data types.
456#[derive(Debug, Clone, PartialEq)]
457pub enum ChiselType {
458    /// `UInt(width.W)` — unsigned integer of `width` bits
459    UInt(u32),
460    /// `SInt(width.W)` — signed integer of `width` bits
461    SInt(u32),
462    /// `Bool()` — single-bit boolean
463    Bool,
464    /// `new Bundle { ... }` — named-field aggregate
465    Bundle(Vec<(String, Box<ChiselType>)>),
466    /// `Vec(count, gen)` — hardware vector
467    Vec(u32, Box<ChiselType>),
468    /// `Clock()` — clock signal
469    Clock,
470    /// `Reset()` — reset signal
471    Reset,
472    /// `AsyncReset()` — asynchronous reset
473    AsyncReset,
474}
475#[allow(dead_code)]
476#[derive(Debug, Clone, Default)]
477pub struct ChiselPassStats {
478    pub total_runs: u32,
479    pub successful_runs: u32,
480    pub total_changes: u64,
481    pub time_ms: u64,
482    pub iterations_used: u32,
483}
484impl ChiselPassStats {
485    #[allow(dead_code)]
486    pub fn new() -> Self {
487        Self::default()
488    }
489    #[allow(dead_code)]
490    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
491        self.total_runs += 1;
492        self.successful_runs += 1;
493        self.total_changes += changes;
494        self.time_ms += time_ms;
495        self.iterations_used = iterations;
496    }
497    #[allow(dead_code)]
498    pub fn average_changes_per_run(&self) -> f64 {
499        if self.total_runs == 0 {
500            return 0.0;
501        }
502        self.total_changes as f64 / self.total_runs as f64
503    }
504    #[allow(dead_code)]
505    pub fn success_rate(&self) -> f64 {
506        if self.total_runs == 0 {
507            return 0.0;
508        }
509        self.successful_runs as f64 / self.total_runs as f64
510    }
511    #[allow(dead_code)]
512    pub fn format_summary(&self) -> String {
513        format!(
514            "Runs: {}/{}, Changes: {}, Time: {}ms",
515            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
516        )
517    }
518}
519/// A Chisel hardware expression.
520#[derive(Debug, Clone, PartialEq)]
521pub enum ChiselExpr {
522    /// Integer literal: `value.U(width.W)`
523    ULit(u64, u32),
524    /// Signed literal: `value.S(width.W)`
525    SLit(i64, u32),
526    /// `true.B` or `false.B`
527    BoolLit(bool),
528    /// Signal reference
529    Var(String),
530    /// `io.name`
531    Io(String),
532    /// `reg.name` (Register field)
533    RegField(String),
534    /// Binary operation: `lhs op rhs`
535    BinOp(Box<ChiselExpr>, String, Box<ChiselExpr>),
536    /// Unary operation: `op(operand)`
537    UnOp(String, Box<ChiselExpr>),
538    /// Mux: `Mux(sel, t, f)`
539    Mux(Box<ChiselExpr>, Box<ChiselExpr>, Box<ChiselExpr>),
540    /// Bit extraction: `expr(hi, lo)`
541    BitSlice(Box<ChiselExpr>, u32, u32),
542    /// Cat: `Cat(a, b, ...)`
543    Cat(Vec<ChiselExpr>),
544    /// Method call: `receiver.method(args...)`
545    MethodCall(Box<ChiselExpr>, String, Vec<ChiselExpr>),
546}
547/// SRAM port type.
548#[allow(dead_code)]
549#[derive(Debug, Clone, PartialEq)]
550pub enum SramPortType {
551    SinglePort,
552    SimpleDualPort,
553    TrueDualPort,
554}
555#[allow(dead_code)]
556#[derive(Debug, Clone, PartialEq)]
557pub enum ChiselPassPhase {
558    Analysis,
559    Transformation,
560    Verification,
561    Cleanup,
562}
563impl ChiselPassPhase {
564    #[allow(dead_code)]
565    pub fn name(&self) -> &str {
566        match self {
567            ChiselPassPhase::Analysis => "analysis",
568            ChiselPassPhase::Transformation => "transformation",
569            ChiselPassPhase::Verification => "verification",
570            ChiselPassPhase::Cleanup => "cleanup",
571        }
572    }
573    #[allow(dead_code)]
574    pub fn is_modifying(&self) -> bool {
575        matches!(
576            self,
577            ChiselPassPhase::Transformation | ChiselPassPhase::Cleanup
578        )
579    }
580}
581#[allow(dead_code)]
582#[derive(Debug, Clone)]
583pub struct ChiselLivenessInfo {
584    pub live_in: Vec<std::collections::HashSet<u32>>,
585    pub live_out: Vec<std::collections::HashSet<u32>>,
586    pub defs: Vec<std::collections::HashSet<u32>>,
587    pub uses: Vec<std::collections::HashSet<u32>>,
588}
589impl ChiselLivenessInfo {
590    #[allow(dead_code)]
591    pub fn new(block_count: usize) -> Self {
592        ChiselLivenessInfo {
593            live_in: vec![std::collections::HashSet::new(); block_count],
594            live_out: vec![std::collections::HashSet::new(); block_count],
595            defs: vec![std::collections::HashSet::new(); block_count],
596            uses: vec![std::collections::HashSet::new(); block_count],
597        }
598    }
599    #[allow(dead_code)]
600    pub fn add_def(&mut self, block: usize, var: u32) {
601        if block < self.defs.len() {
602            self.defs[block].insert(var);
603        }
604    }
605    #[allow(dead_code)]
606    pub fn add_use(&mut self, block: usize, var: u32) {
607        if block < self.uses.len() {
608            self.uses[block].insert(var);
609        }
610    }
611    #[allow(dead_code)]
612    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
613        self.live_in
614            .get(block)
615            .map(|s| s.contains(&var))
616            .unwrap_or(false)
617    }
618    #[allow(dead_code)]
619    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
620        self.live_out
621            .get(block)
622            .map(|s| s.contains(&var))
623            .unwrap_or(false)
624    }
625}
626/// Code-generation backend for Chisel 3 / Chisel 5.
627#[derive(Debug, Clone, Default)]
628pub struct ChiselBackend;
629impl ChiselBackend {
630    /// Create a new `ChiselBackend`.
631    pub fn new() -> Self {
632        ChiselBackend
633    }
634    /// Emit the Chisel type string for a `ChiselType`.
635    pub fn emit_type(&self, ty: &ChiselType) -> String {
636        ty.to_string()
637    }
638    /// Emit the `val io = IO(new Bundle { ... })` declaration for a port list.
639    pub fn io_bundle(&self, ports: &[ChiselPort]) -> String {
640        let mut s = String::from("val io = IO(new Bundle {\n");
641        for port in ports {
642            s.push_str(&format!(
643                "    val {name} = {dir}({ty})\n",
644                name = port.name,
645                dir = port.direction(),
646                ty = self.emit_type(&port.ty),
647            ));
648        }
649        s.push_str("  })");
650        s
651    }
652    /// Emit a complete Chisel module class.
653    pub fn emit_module(&self, module: &ChiselModule) -> String {
654        let mut out = String::new();
655        out.push_str("// Generated by OxiLean ChiselBackend\n");
656        out.push_str("import chisel3._\n");
657        out.push_str("import chisel3.util._\n\n");
658        out.push_str(&format!("class {} extends Module {{\n", module.name));
659        if !module.ports.is_empty() {
660            out.push_str("  ");
661            out.push_str(&self.io_bundle(&module.ports));
662            out.push_str("\n\n");
663        }
664        for stmt in &module.body {
665            out.push_str("  ");
666            out.push_str(stmt);
667            out.push('\n');
668        }
669        out.push_str("}\n");
670        out
671    }
672    /// Emit a `when` block: `when(cond) { body }`.
673    pub fn when_stmt(&self, cond: &str, body: &str) -> String {
674        format!("when ({cond}) {{\n    {body}\n  }}")
675    }
676    /// Emit a `when` / `otherwise` block.
677    pub fn when_otherwise(&self, cond: &str, when_body: &str, other_body: &str) -> String {
678        format!("when ({cond}) {{\n    {when_body}\n  }} .otherwise {{\n    {other_body}\n  }}")
679    }
680    /// Emit a register declaration: `val r = RegInit(init_val)`.
681    pub fn reg_init(&self, name: &str, ty: &ChiselType, init: &str) -> String {
682        format!("val {name} = RegInit({init}.U.asTypeOf({ty}))")
683    }
684    /// Emit a register declaration without reset: `val r = Reg(gen)`.
685    pub fn reg_no_reset(&self, name: &str, ty: &ChiselType) -> String {
686        format!("val {name} = Reg({ty})")
687    }
688    /// Emit a wire declaration: `val w = Wire(gen)`.
689    pub fn wire_decl(&self, name: &str, ty: &ChiselType) -> String {
690        format!("val {name} = Wire({ty})")
691    }
692    /// Emit a connection: `lhs := rhs`.
693    pub fn connect(&self, lhs: &str, rhs: &str) -> String {
694        format!("{lhs} := {rhs}")
695    }
696    /// Emit a `printf` debug statement.
697    pub fn printf(&self, fmt_str: &str, args: &[&str]) -> String {
698        if args.is_empty() {
699            format!("printf(\"{fmt_str}\\n\")")
700        } else {
701            let arg_str = args.join(", ");
702            format!("printf(\"{fmt_str}\\n\", {arg_str})")
703        }
704    }
705    /// Emit a `assert` statement.
706    pub fn assert_stmt(&self, cond: &str, msg: &str) -> String {
707        format!("assert({cond}, \"{msg}\")")
708    }
709    /// Emit a `Mux` expression string.
710    pub fn mux_expr(&self, sel: &str, t: &str, f: &str) -> String {
711        format!("Mux({sel}, {t}, {f})")
712    }
713    /// Emit a `Cat` expression string.
714    pub fn cat_expr(&self, parts: &[&str]) -> String {
715        format!("Cat({})", parts.join(", "))
716    }
717    /// Emit a `fill` (replicate) expression.
718    pub fn fill_expr(&self, count: u32, value: &str) -> String {
719        format!("Fill({count}, {value})")
720    }
721    /// Emit a submodule instantiation.
722    pub fn instantiate(&self, class: &str, inst_name: &str) -> String {
723        format!("val {inst_name} = Module(new {class}())")
724    }
725    /// Emit a `ChiselExpr` as a string.
726    pub fn emit_expr(&self, expr: &ChiselExpr) -> String {
727        expr.to_string()
728    }
729}
730impl ChiselBackend {
731    /// Emit a DontCare assignment.
732    #[allow(dead_code)]
733    pub fn dont_care(&self, signal: &str) -> String {
734        format!("{} := DontCare", signal)
735    }
736    /// Emit an Irrevocable (valid+ready, never de-asserts valid) port.
737    #[allow(dead_code)]
738    pub fn irrevocable_port(&self, name: &str, data_type: &ChiselType, is_output: bool) -> String {
739        let dir = if is_output {
740            "Irrevocable"
741        } else {
742            "Flipped(Irrevocable"
743        };
744        let close = if is_output { "" } else { ")" };
745        format!("val {} = {}({}){}", name, dir, data_type, close)
746    }
747    /// Emit a combinational ROM using a Vec\[UInt\] initialized from a list.
748    #[allow(dead_code)]
749    pub fn comb_rom(&self, name: &str, _data_type: &ChiselType, values: &[&str]) -> String {
750        let entries: Vec<String> = values.iter().map(|v| format!("{}.U", v)).collect();
751        format!("val {} = VecInit(Seq({}))\n", name, entries.join(", "))
752    }
753    /// Emit a Mux1H (one-hot mux).
754    #[allow(dead_code)]
755    pub fn mux1h(&self, _sel: &str, cases: &[(&str, &str)]) -> String {
756        let entries: Vec<String> = cases
757            .iter()
758            .map(|(s, v)| format!("{} -> {}", s, v))
759            .collect();
760        format!("Mux1H(Seq({}))", entries.join(", "))
761    }
762    /// Emit a log2Ceil computation.
763    #[allow(dead_code)]
764    pub fn log2_ceil(&self, n: u32) -> u32 {
765        if n <= 1 {
766            1
767        } else {
768            (n as f64).log2().ceil() as u32
769        }
770    }
771    /// Emit a log2Floor computation.
772    #[allow(dead_code)]
773    pub fn log2_floor(&self, n: u32) -> u32 {
774        if n == 0 {
775            0
776        } else {
777            (n as f64).log2().floor() as u32
778        }
779    }
780    /// Check if n is a power of two.
781    #[allow(dead_code)]
782    pub fn is_pow2(&self, n: u32) -> bool {
783        n > 0 && (n & (n - 1)) == 0
784    }
785    /// Emit a cover statement (Chisel formal verification).
786    #[allow(dead_code)]
787    pub fn cover_stmt(&self, cond: &str, msg: &str) -> String {
788        format!("cover({}, \"{}\", \"{}\")", cond, msg, msg)
789    }
790    /// Emit an assume statement.
791    #[allow(dead_code)]
792    pub fn assume_stmt(&self, cond: &str) -> String {
793        format!("assume({}.B, \"assumption\")", cond)
794    }
795    /// Emit a ChiselSim-compatible printf for simulation only.
796    #[allow(dead_code)]
797    pub fn sim_printf(&self, fmt_str: &str, args: &[&str]) -> String {
798        if args.is_empty() {
799            format!("printf(p\"{}\", cf\"\")", fmt_str)
800        } else {
801            format!("printf(p\"{}\", {})", fmt_str, args.join(", "))
802        }
803    }
804    /// Emit a chisel3.util.Fill call (replicate a bit pattern).
805    #[allow(dead_code)]
806    pub fn fill(&self, n: u32, expr: &str) -> String {
807        format!("Fill({}, {})", n, expr)
808    }
809    /// Emit a Cat call (concatenate signals).
810    #[allow(dead_code)]
811    pub fn cat(&self, signals: &[&str]) -> String {
812        format!("Cat({})", signals.join(", "))
813    }
814    /// Emit a PopCount call.
815    #[allow(dead_code)]
816    pub fn popcount(&self, signal: &str) -> String {
817        format!("PopCount({})", signal)
818    }
819    /// Emit a priority encoder (OHToUInt).
820    #[allow(dead_code)]
821    pub fn oh_to_uint(&self, one_hot: &str) -> String {
822        format!("OHToUInt({})", one_hot)
823    }
824    /// Emit a UIntToOH call.
825    #[allow(dead_code)]
826    pub fn uint_to_oh(&self, n: &str, width: u32) -> String {
827        format!("UIntToOH({}, {})", n, width)
828    }
829    /// Emit a Reverse call (bit-reversal).
830    #[allow(dead_code)]
831    pub fn reverse(&self, signal: &str) -> String {
832        format!("Reverse({})", signal)
833    }
834    /// Emit a MuxCase expression.
835    #[allow(dead_code)]
836    pub fn mux_case(&self, default: &str, cases: &[(&str, &str)]) -> String {
837        let entries: Vec<String> = cases
838            .iter()
839            .map(|(c, v)| format!("({}) -> ({})", c, v))
840            .collect();
841        format!("MuxCase({}, Seq({}))", default, entries.join(", "))
842    }
843    /// Emit a ShiftRegister instantiation.
844    #[allow(dead_code)]
845    pub fn shift_register(&self, data: &str, n: u32, reset_val: &str) -> String {
846        format!("ShiftRegister({}, {}, {}.U)", data, n, reset_val)
847    }
848    /// Emit an arbiter (RRArbiter) for n inputs of a given type.
849    #[allow(dead_code)]
850    pub fn round_robin_arbiter(&self, name: &str, data_type: &ChiselType, n: u32) -> String {
851        format!(
852            "val {} = Module(new RRArbiter({}, {}))\n",
853            name, data_type, n
854        )
855    }
856    /// Emit a fixed-priority arbiter.
857    #[allow(dead_code)]
858    pub fn priority_arbiter(&self, name: &str, data_type: &ChiselType, n: u32) -> String {
859        format!("val {} = Module(new Arbiter({}, {}))\n", name, data_type, n)
860    }
861    /// Emit a Chisel FIFO queue module instantiation.
862    #[allow(dead_code)]
863    pub fn queue_module(&self, name: &str, data_type: &ChiselType, depth: u32) -> String {
864        format!(
865            "val {} = Module(new Queue({}, {}))\n",
866            name, data_type, depth
867        )
868    }
869    /// Emit a when/elsewhen/otherwise chain.
870    #[allow(dead_code)]
871    pub fn when_chain(&self, cond_body: &[(&str, &str)], otherwise: Option<&str>) -> String {
872        let mut out = String::new();
873        for (i, (cond, body)) in cond_body.iter().enumerate() {
874            if i == 0 {
875                out.push_str(&format!("when ({}) {{\n  {}\n}}", cond, body));
876            } else {
877                out.push_str(&format!(".elsewhen ({}) {{\n  {}\n}}", cond, body));
878            }
879        }
880        if let Some(other) = otherwise {
881            out.push_str(&format!(".otherwise {{\n  {}\n}}", other));
882        }
883        out.push('\n');
884        out
885    }
886    /// Emit a counter that wraps at max_val.
887    #[allow(dead_code)]
888    pub fn counter(&self, name: &str, max_val: u32, enable: &str) -> String {
889        let width = self.log2_ceil(max_val + 1);
890        format!(
891            "val ({}_count, {}_wrap) = Counter({}, {})\n",
892            name, name, enable, max_val
893        ) + &format!("// {} is {}-bit counter up to {}\n", name, width, max_val)
894    }
895    /// Emit a reset synchronizer (2-FF synchronizer for reset deassertion).
896    #[allow(dead_code)]
897    pub fn reset_sync(&self, name: &str, async_rst: &str) -> String {
898        format!(
899            "val {}_sync = withReset({}.asAsyncReset) {{\n  RegNext(RegNext(true.B, false.B), false.B)\n}}\n",
900            name, async_rst
901        )
902    }
903    /// Emit a clock domain crossing (CDC) handshake wrapper comment.
904    #[allow(dead_code)]
905    pub fn cdc_handshake_comment(&self, from_clk: &str, to_clk: &str) -> String {
906        format!(
907            "/* CDC: {} -> {} — use async FIFO or req/ack handshake here */\n",
908            from_clk, to_clk
909        )
910    }
911    /// Emit a BlackBox module class stub.
912    #[allow(dead_code)]
913    pub fn blackbox_stub(&self, name: &str, params: &[(&str, &str)]) -> String {
914        let param_str: Vec<String> = params
915            .iter()
916            .map(|(k, v)| format!("\"{}\" -> {}", k, v))
917            .collect();
918        if params.is_empty() {
919            format!(
920                "class {} extends BlackBox {{\n  val io = IO(new Bundle {{}})\n}}\n",
921                name
922            )
923        } else {
924            format!(
925                "class {} extends BlackBox(Map({})) {{\n  val io = IO(new Bundle {{}})\n}}\n",
926                name,
927                param_str.join(", ")
928            )
929        }
930    }
931}
932#[allow(dead_code)]
933pub struct ChiselConstantFoldingHelper;
934impl ChiselConstantFoldingHelper {
935    #[allow(dead_code)]
936    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
937        a.checked_add(b)
938    }
939    #[allow(dead_code)]
940    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
941        a.checked_sub(b)
942    }
943    #[allow(dead_code)]
944    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
945        a.checked_mul(b)
946    }
947    #[allow(dead_code)]
948    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
949        if b == 0 {
950            None
951        } else {
952            a.checked_div(b)
953        }
954    }
955    #[allow(dead_code)]
956    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
957        a + b
958    }
959    #[allow(dead_code)]
960    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
961        a * b
962    }
963    #[allow(dead_code)]
964    pub fn fold_neg_i64(a: i64) -> Option<i64> {
965        a.checked_neg()
966    }
967    #[allow(dead_code)]
968    pub fn fold_not_bool(a: bool) -> bool {
969        !a
970    }
971    #[allow(dead_code)]
972    pub fn fold_and_bool(a: bool, b: bool) -> bool {
973        a && b
974    }
975    #[allow(dead_code)]
976    pub fn fold_or_bool(a: bool, b: bool) -> bool {
977        a || b
978    }
979    #[allow(dead_code)]
980    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
981        a.checked_shl(b)
982    }
983    #[allow(dead_code)]
984    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
985        a.checked_shr(b)
986    }
987    #[allow(dead_code)]
988    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
989        if b == 0 {
990            None
991        } else {
992            Some(a % b)
993        }
994    }
995    #[allow(dead_code)]
996    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
997        a & b
998    }
999    #[allow(dead_code)]
1000    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1001        a | b
1002    }
1003    #[allow(dead_code)]
1004    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1005        a ^ b
1006    }
1007    #[allow(dead_code)]
1008    pub fn fold_bitnot_i64(a: i64) -> i64 {
1009        !a
1010    }
1011}
1012/// Generates a multi-stage pipeline register chain.
1013#[allow(dead_code)]
1014#[derive(Debug, Clone)]
1015pub struct ChiselPipelineRegisterChain {
1016    pub stages: Vec<PipelineStage>,
1017    pub reset_val: String,
1018}
1019#[allow(dead_code)]
1020impl ChiselPipelineRegisterChain {
1021    pub fn new(reset_val: impl Into<String>) -> Self {
1022        Self {
1023            stages: Vec::new(),
1024            reset_val: reset_val.into(),
1025        }
1026    }
1027    pub fn stage(mut self, s: PipelineStage) -> Self {
1028        self.stages.push(s);
1029        self
1030    }
1031    /// Emit Chisel register declarations for all stages.
1032    pub fn emit_registers(&self) -> String {
1033        let mut out = String::new();
1034        for (i, stage) in self.stages.iter().enumerate() {
1035            out.push_str(&format!(
1036                "val {} = RegInit({}.U.asTypeOf({}))\n",
1037                stage.name, self.reset_val, stage.data_type
1038            ));
1039            if stage.has_valid {
1040                out.push_str(&format!("val {}_valid = RegInit(false.B)\n", stage.name));
1041            }
1042            if stage.has_stall {
1043                out.push_str(&format!(
1044                    "val {}_stall = WireDefault(false.B)\n",
1045                    stage.name
1046                ));
1047            }
1048            if i + 1 < self.stages.len() {
1049                let next = &self.stages[i + 1];
1050                out.push_str(&format!(
1051                    "when (!{}_stall) {{ {} := {} }}\n",
1052                    next.name, next.name, stage.name
1053                ));
1054                if stage.has_valid && next.has_valid {
1055                    out.push_str(&format!(
1056                        "when (!{}_stall) {{ {}_valid := {}_valid }}\n",
1057                        next.name, next.name, stage.name
1058                    ));
1059                }
1060            }
1061        }
1062        out
1063    }
1064    pub fn stage_count(&self) -> usize {
1065        self.stages.len()
1066    }
1067}
1068#[allow(dead_code)]
1069#[derive(Debug, Clone)]
1070pub struct ChiselWorklist {
1071    pub(super) items: std::collections::VecDeque<u32>,
1072    pub(super) in_worklist: std::collections::HashSet<u32>,
1073}
1074impl ChiselWorklist {
1075    #[allow(dead_code)]
1076    pub fn new() -> Self {
1077        ChiselWorklist {
1078            items: std::collections::VecDeque::new(),
1079            in_worklist: std::collections::HashSet::new(),
1080        }
1081    }
1082    #[allow(dead_code)]
1083    pub fn push(&mut self, item: u32) -> bool {
1084        if self.in_worklist.insert(item) {
1085            self.items.push_back(item);
1086            true
1087        } else {
1088            false
1089        }
1090    }
1091    #[allow(dead_code)]
1092    pub fn pop(&mut self) -> Option<u32> {
1093        let item = self.items.pop_front()?;
1094        self.in_worklist.remove(&item);
1095        Some(item)
1096    }
1097    #[allow(dead_code)]
1098    pub fn is_empty(&self) -> bool {
1099        self.items.is_empty()
1100    }
1101    #[allow(dead_code)]
1102    pub fn len(&self) -> usize {
1103        self.items.len()
1104    }
1105    #[allow(dead_code)]
1106    pub fn contains(&self, item: u32) -> bool {
1107        self.in_worklist.contains(&item)
1108    }
1109}
1110#[allow(dead_code)]
1111#[derive(Debug, Clone)]
1112pub struct ChiselDominatorTree {
1113    pub idom: Vec<Option<u32>>,
1114    pub dom_children: Vec<Vec<u32>>,
1115    pub dom_depth: Vec<u32>,
1116}
1117impl ChiselDominatorTree {
1118    #[allow(dead_code)]
1119    pub fn new(size: usize) -> Self {
1120        ChiselDominatorTree {
1121            idom: vec![None; size],
1122            dom_children: vec![Vec::new(); size],
1123            dom_depth: vec![0; size],
1124        }
1125    }
1126    #[allow(dead_code)]
1127    pub fn set_idom(&mut self, node: usize, idom: u32) {
1128        self.idom[node] = Some(idom);
1129    }
1130    #[allow(dead_code)]
1131    pub fn dominates(&self, a: usize, b: usize) -> bool {
1132        if a == b {
1133            return true;
1134        }
1135        let mut cur = b;
1136        loop {
1137            match self.idom[cur] {
1138                Some(parent) if parent as usize == a => return true,
1139                Some(parent) if parent as usize == cur => return false,
1140                Some(parent) => cur = parent as usize,
1141                None => return false,
1142            }
1143        }
1144    }
1145    #[allow(dead_code)]
1146    pub fn depth(&self, node: usize) -> u32 {
1147        self.dom_depth.get(node).copied().unwrap_or(0)
1148    }
1149}
1150/// A single I/O port in a Chisel module's IO bundle.
1151#[derive(Debug, Clone, PartialEq)]
1152pub struct ChiselPort {
1153    /// Port name
1154    pub name: String,
1155    /// Chisel hardware type
1156    pub ty: ChiselType,
1157    /// `true` → `Output(...)`, `false` → `Input(...)`
1158    pub is_output: bool,
1159}
1160impl ChiselPort {
1161    /// Create an input port.
1162    pub fn input(name: impl Into<String>, ty: ChiselType) -> Self {
1163        ChiselPort {
1164            name: name.into(),
1165            ty,
1166            is_output: false,
1167        }
1168    }
1169    /// Create an output port.
1170    pub fn output(name: impl Into<String>, ty: ChiselType) -> Self {
1171        ChiselPort {
1172            name: name.into(),
1173            ty,
1174            is_output: true,
1175        }
1176    }
1177    /// Direction keyword: `"Input"` or `"Output"`.
1178    pub fn direction(&self) -> &'static str {
1179        if self.is_output {
1180            "Output"
1181        } else {
1182            "Input"
1183        }
1184    }
1185}
1186/// AXI-Stream endpoint direction.
1187#[allow(dead_code)]
1188#[derive(Debug, Clone, PartialEq)]
1189pub enum StreamDirection {
1190    Producer,
1191    Consumer,
1192}
1193/// A module wrapper with AXI-Stream interface(s).
1194#[allow(dead_code)]
1195#[derive(Debug, Clone)]
1196pub struct ChiselStreamingModule {
1197    pub name: String,
1198    pub data_width: u32,
1199    pub direction: StreamDirection,
1200    pub has_tlast: bool,
1201    pub has_tkeep: bool,
1202    pub has_tid: bool,
1203    pub id_width: u32,
1204    pub has_tuser: bool,
1205    pub user_width: u32,
1206    pub body: Vec<String>,
1207}
1208#[allow(dead_code)]
1209impl ChiselStreamingModule {
1210    pub fn producer(name: impl Into<String>, data_width: u32) -> Self {
1211        Self {
1212            name: name.into(),
1213            data_width,
1214            direction: StreamDirection::Producer,
1215            has_tlast: false,
1216            has_tkeep: false,
1217            has_tid: false,
1218            id_width: 4,
1219            has_tuser: false,
1220            user_width: 1,
1221            body: Vec::new(),
1222        }
1223    }
1224    pub fn consumer(name: impl Into<String>, data_width: u32) -> Self {
1225        let mut m = Self::producer(name, data_width);
1226        m.direction = StreamDirection::Consumer;
1227        m
1228    }
1229    pub fn with_tlast(mut self) -> Self {
1230        self.has_tlast = true;
1231        self
1232    }
1233    pub fn with_tkeep(mut self) -> Self {
1234        self.has_tkeep = true;
1235        self
1236    }
1237    pub fn with_tid(mut self, width: u32) -> Self {
1238        self.has_tid = true;
1239        self.id_width = width;
1240        self
1241    }
1242    pub fn with_tuser(mut self, width: u32) -> Self {
1243        self.has_tuser = true;
1244        self.user_width = width;
1245        self
1246    }
1247    pub fn add_stmt(mut self, s: impl Into<String>) -> Self {
1248        self.body.push(s.into());
1249        self
1250    }
1251    pub fn emit(&self) -> String {
1252        let is_prod = self.direction == StreamDirection::Producer;
1253        let tdata_dir = if is_prod { "Output" } else { "Input" };
1254        let tvalid_dir = if is_prod { "Output" } else { "Input" };
1255        let tready_dir = if is_prod { "Input" } else { "Output" };
1256        let mut out = format!(
1257            "class {} extends Module {{\n  val io = IO(new Bundle {{\n",
1258            self.name
1259        );
1260        out.push_str(&format!(
1261            "    val tdata  = {}(UInt({}.W))\n",
1262            tdata_dir, self.data_width
1263        ));
1264        out.push_str(&format!("    val tvalid = {}(Bool())\n", tvalid_dir));
1265        out.push_str(&format!("    val tready = {}(Bool())\n", tready_dir));
1266        if self.has_tlast {
1267            out.push_str(&format!("    val tlast  = {}(Bool())\n", tdata_dir));
1268        }
1269        if self.has_tkeep {
1270            let kw = self.data_width / 8;
1271            out.push_str(&format!("    val tkeep  = {}(UInt({}.W))\n", tdata_dir, kw));
1272        }
1273        if self.has_tid {
1274            out.push_str(&format!(
1275                "    val tid    = {}(UInt({}.W))\n",
1276                tdata_dir, self.id_width
1277            ));
1278        }
1279        if self.has_tuser {
1280            out.push_str(&format!(
1281                "    val tuser  = {}(UInt({}.W))\n",
1282                tdata_dir, self.user_width
1283            ));
1284        }
1285        out.push_str("  })\n\n");
1286        for stmt in &self.body {
1287            out.push_str(&format!("  {}\n", stmt));
1288        }
1289        out.push_str("}\n");
1290        out
1291    }
1292}
1293/// A Chisel Decoupled / ReadyValid interface bundle descriptor.
1294#[allow(dead_code)]
1295#[derive(Debug, Clone)]
1296pub struct ChiselReadyValidBundle {
1297    pub data_type: ChiselType,
1298    pub has_last: bool,
1299    pub has_keep: bool,
1300    pub keep_width: u32,
1301}
1302#[allow(dead_code)]
1303impl ChiselReadyValidBundle {
1304    pub fn new(data_type: ChiselType) -> Self {
1305        Self {
1306            data_type,
1307            has_last: false,
1308            has_keep: false,
1309            keep_width: 0,
1310        }
1311    }
1312    pub fn with_last(mut self) -> Self {
1313        self.has_last = true;
1314        self
1315    }
1316    pub fn with_keep(mut self, width: u32) -> Self {
1317        self.has_keep = true;
1318        self.keep_width = width;
1319        self
1320    }
1321    /// Emit the Decoupled\[T\] port declaration.
1322    pub fn emit_decoupled(&self, name: &str, is_output: bool) -> String {
1323        let dir = if is_output {
1324            "Decoupled"
1325        } else {
1326            "Flipped(Decoupled"
1327        };
1328        let close = if is_output { "" } else { ")" };
1329        let mut out = format!("    val {} = {}({}){}\n", name, dir, self.data_type, close);
1330        if self.has_last {
1331            out.push_str(&format!(
1332                "    val {}_last = if (is_output) Output(Bool()) else Input(Bool())\n",
1333                name
1334            ));
1335        }
1336        if self.has_keep {
1337            out.push_str(&format!(
1338                "    val {}_keep = if (is_output) Output(UInt({}.W)) else Input(UInt({}.W))\n",
1339                name, self.keep_width, self.keep_width
1340            ));
1341        }
1342        out
1343    }
1344    /// Emit fire condition (valid && ready).
1345    pub fn emit_fire(&self, port_name: &str) -> String {
1346        format!(
1347            "val {}_fire = {}.valid && {}.ready\n",
1348            port_name, port_name, port_name
1349        )
1350    }
1351    /// Emit a queue buffer for this channel.
1352    pub fn emit_queue(&self, input: &str, output: &str, depth: u32) -> String {
1353        format!(
1354            "val {}_q = Queue({}, {})\n{}_q <> {}\n{} <> {}_q\n",
1355            input, input, depth, input, input, output, input
1356        )
1357    }
1358}
1359/// A complete Chisel module definition.
1360#[derive(Debug, Clone)]
1361pub struct ChiselModule {
1362    /// Class name for the Chisel module
1363    pub name: String,
1364    /// I/O ports
1365    pub ports: Vec<ChiselPort>,
1366    /// Body statements (pre-formatted Scala strings)
1367    pub body: Vec<String>,
1368}
1369impl ChiselModule {
1370    /// Construct a new, empty Chisel module.
1371    pub fn new(name: impl Into<String>) -> Self {
1372        ChiselModule {
1373            name: name.into(),
1374            ports: Vec::new(),
1375            body: Vec::new(),
1376        }
1377    }
1378    /// Add an input port.
1379    pub fn add_input(&mut self, name: impl Into<String>, ty: ChiselType) {
1380        self.ports.push(ChiselPort::input(name, ty));
1381    }
1382    /// Add an output port.
1383    pub fn add_output(&mut self, name: impl Into<String>, ty: ChiselType) {
1384        self.ports.push(ChiselPort::output(name, ty));
1385    }
1386    /// Append a body statement.
1387    pub fn add_stmt(&mut self, stmt: impl Into<String>) {
1388        self.body.push(stmt.into());
1389    }
1390}
1391#[allow(dead_code)]
1392#[derive(Debug, Clone)]
1393pub struct ChiselPassConfig {
1394    pub phase: ChiselPassPhase,
1395    pub enabled: bool,
1396    pub max_iterations: u32,
1397    pub debug_output: bool,
1398    pub pass_name: String,
1399}
1400impl ChiselPassConfig {
1401    #[allow(dead_code)]
1402    pub fn new(name: impl Into<String>, phase: ChiselPassPhase) -> Self {
1403        ChiselPassConfig {
1404            phase,
1405            enabled: true,
1406            max_iterations: 10,
1407            debug_output: false,
1408            pass_name: name.into(),
1409        }
1410    }
1411    #[allow(dead_code)]
1412    pub fn disabled(mut self) -> Self {
1413        self.enabled = false;
1414        self
1415    }
1416    #[allow(dead_code)]
1417    pub fn with_debug(mut self) -> Self {
1418        self.debug_output = true;
1419        self
1420    }
1421    #[allow(dead_code)]
1422    pub fn max_iter(mut self, n: u32) -> Self {
1423        self.max_iterations = n;
1424        self
1425    }
1426}
1427/// Describes one stage of a pipeline register chain.
1428#[allow(dead_code)]
1429#[derive(Debug, Clone)]
1430pub struct PipelineStage {
1431    pub name: String,
1432    pub data_type: ChiselType,
1433    pub has_valid: bool,
1434    pub has_stall: bool,
1435}
1436#[allow(dead_code)]
1437impl PipelineStage {
1438    pub fn new(name: impl Into<String>, data_type: ChiselType) -> Self {
1439        Self {
1440            name: name.into(),
1441            data_type,
1442            has_valid: false,
1443            has_stall: false,
1444        }
1445    }
1446    pub fn with_valid(mut self) -> Self {
1447        self.has_valid = true;
1448        self
1449    }
1450    pub fn with_stall(mut self) -> Self {
1451        self.has_stall = true;
1452        self
1453    }
1454}