1use 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#[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 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#[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 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#[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#[derive(Debug, Clone, PartialEq)]
457pub enum ChiselType {
458 UInt(u32),
460 SInt(u32),
462 Bool,
464 Bundle(Vec<(String, Box<ChiselType>)>),
466 Vec(u32, Box<ChiselType>),
468 Clock,
470 Reset,
472 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#[derive(Debug, Clone, PartialEq)]
521pub enum ChiselExpr {
522 ULit(u64, u32),
524 SLit(i64, u32),
526 BoolLit(bool),
528 Var(String),
530 Io(String),
532 RegField(String),
534 BinOp(Box<ChiselExpr>, String, Box<ChiselExpr>),
536 UnOp(String, Box<ChiselExpr>),
538 Mux(Box<ChiselExpr>, Box<ChiselExpr>, Box<ChiselExpr>),
540 BitSlice(Box<ChiselExpr>, u32, u32),
542 Cat(Vec<ChiselExpr>),
544 MethodCall(Box<ChiselExpr>, String, Vec<ChiselExpr>),
546}
547#[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#[derive(Debug, Clone, Default)]
628pub struct ChiselBackend;
629impl ChiselBackend {
630 pub fn new() -> Self {
632 ChiselBackend
633 }
634 pub fn emit_type(&self, ty: &ChiselType) -> String {
636 ty.to_string()
637 }
638 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 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 pub fn when_stmt(&self, cond: &str, body: &str) -> String {
674 format!("when ({cond}) {{\n {body}\n }}")
675 }
676 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 pub fn reg_init(&self, name: &str, ty: &ChiselType, init: &str) -> String {
682 format!("val {name} = RegInit({init}.U.asTypeOf({ty}))")
683 }
684 pub fn reg_no_reset(&self, name: &str, ty: &ChiselType) -> String {
686 format!("val {name} = Reg({ty})")
687 }
688 pub fn wire_decl(&self, name: &str, ty: &ChiselType) -> String {
690 format!("val {name} = Wire({ty})")
691 }
692 pub fn connect(&self, lhs: &str, rhs: &str) -> String {
694 format!("{lhs} := {rhs}")
695 }
696 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 pub fn assert_stmt(&self, cond: &str, msg: &str) -> String {
707 format!("assert({cond}, \"{msg}\")")
708 }
709 pub fn mux_expr(&self, sel: &str, t: &str, f: &str) -> String {
711 format!("Mux({sel}, {t}, {f})")
712 }
713 pub fn cat_expr(&self, parts: &[&str]) -> String {
715 format!("Cat({})", parts.join(", "))
716 }
717 pub fn fill_expr(&self, count: u32, value: &str) -> String {
719 format!("Fill({count}, {value})")
720 }
721 pub fn instantiate(&self, class: &str, inst_name: &str) -> String {
723 format!("val {inst_name} = Module(new {class}())")
724 }
725 pub fn emit_expr(&self, expr: &ChiselExpr) -> String {
727 expr.to_string()
728 }
729}
730impl ChiselBackend {
731 #[allow(dead_code)]
733 pub fn dont_care(&self, signal: &str) -> String {
734 format!("{} := DontCare", signal)
735 }
736 #[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 #[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 #[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 #[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 #[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 #[allow(dead_code)]
782 pub fn is_pow2(&self, n: u32) -> bool {
783 n > 0 && (n & (n - 1)) == 0
784 }
785 #[allow(dead_code)]
787 pub fn cover_stmt(&self, cond: &str, msg: &str) -> String {
788 format!("cover({}, \"{}\", \"{}\")", cond, msg, msg)
789 }
790 #[allow(dead_code)]
792 pub fn assume_stmt(&self, cond: &str) -> String {
793 format!("assume({}.B, \"assumption\")", cond)
794 }
795 #[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 #[allow(dead_code)]
806 pub fn fill(&self, n: u32, expr: &str) -> String {
807 format!("Fill({}, {})", n, expr)
808 }
809 #[allow(dead_code)]
811 pub fn cat(&self, signals: &[&str]) -> String {
812 format!("Cat({})", signals.join(", "))
813 }
814 #[allow(dead_code)]
816 pub fn popcount(&self, signal: &str) -> String {
817 format!("PopCount({})", signal)
818 }
819 #[allow(dead_code)]
821 pub fn oh_to_uint(&self, one_hot: &str) -> String {
822 format!("OHToUInt({})", one_hot)
823 }
824 #[allow(dead_code)]
826 pub fn uint_to_oh(&self, n: &str, width: u32) -> String {
827 format!("UIntToOH({}, {})", n, width)
828 }
829 #[allow(dead_code)]
831 pub fn reverse(&self, signal: &str) -> String {
832 format!("Reverse({})", signal)
833 }
834 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[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 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#[derive(Debug, Clone, PartialEq)]
1152pub struct ChiselPort {
1153 pub name: String,
1155 pub ty: ChiselType,
1157 pub is_output: bool,
1159}
1160impl ChiselPort {
1161 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 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 pub fn direction(&self) -> &'static str {
1179 if self.is_output {
1180 "Output"
1181 } else {
1182 "Input"
1183 }
1184 }
1185}
1186#[allow(dead_code)]
1188#[derive(Debug, Clone, PartialEq)]
1189pub enum StreamDirection {
1190 Producer,
1191 Consumer,
1192}
1193#[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#[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 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 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 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#[derive(Debug, Clone)]
1361pub struct ChiselModule {
1362 pub name: String,
1364 pub ports: Vec<ChiselPort>,
1366 pub body: Vec<String>,
1368}
1369impl ChiselModule {
1370 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 pub fn add_input(&mut self, name: impl Into<String>, ty: ChiselType) {
1380 self.ports.push(ChiselPort::input(name, ty));
1381 }
1382 pub fn add_output(&mut self, name: impl Into<String>, ty: ChiselType) {
1384 self.ports.push(ChiselPort::output(name, ty));
1385 }
1386 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#[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}