1use crate::ir::graph::*;
14
15pub fn emit(graph: &ScGraph) -> Result<String, String> {
17 let mut mlir = String::new();
18
19 mlir.push_str(&format!(
20 "// Auto-generated by SC-NeuroCore MLIR Emitter v3.11\n\
21 // Source graph: {}\n\n",
22 graph.name
23 ));
24
25 let mut ports = Vec::new();
27 ports.push("in %clk: i1".to_string());
28 ports.push("in %rst_n: i1".to_string());
29
30 for op in &graph.ops {
31 match op {
32 ScOp::Input { name, .. } => {
33 ports.push(format!("in %{name}: i1"));
34 }
35 ScOp::Output { name, .. } => {
36 ports.push(format!("out {name}: i1"));
37 }
38 _ => {}
39 }
40 }
41
42 mlir.push_str(&format!(
43 "hw.module @{}({}) {{\n",
44 graph.name,
45 ports.join(", ")
46 ));
47
48 let mut last_output = String::from("%clk");
49
50 for op in &graph.ops {
51 match op {
52 ScOp::Input { .. } => {}
53 ScOp::Output { name, source, .. } => {
54 let src = value_wire(graph, *source);
55 mlir.push_str(&format!(" hw.output {src} : i1\n"));
56 last_output = format!("%{name}");
57 }
58 ScOp::Encode {
59 id, prob, seed: _, ..
60 } => {
61 let prob_wire = value_wire(graph, *prob);
62 mlir.push_str(&format!(
63 " %v{} = hw.instance \"enc_{}\" @sc_bitstream_encoder(\
64 clk: %clk: i1, rst_n: %rst_n: i1, x_value: {}: i1) -> (bit_out: i1)\n",
65 id.0, id.0, prob_wire
66 ));
67 last_output = format!("%v{}", id.0);
68 }
69 ScOp::BitwiseAnd { id, lhs, rhs } => {
70 let l = value_wire(graph, *lhs);
71 let r = value_wire(graph, *rhs);
72 mlir.push_str(&format!(" %v{} = comb.and {l}, {r} : i1\n", id.0));
73 last_output = format!("%v{}", id.0);
74 }
75 ScOp::BitwiseXor { id, lhs, rhs } => {
76 let l = value_wire(graph, *lhs);
77 let r = value_wire(graph, *rhs);
78 mlir.push_str(&format!(" %v{} = comb.xor {l}, {r} : i1\n", id.0));
79 last_output = format!("%v{}", id.0);
80 }
81 ScOp::Constant { id, value, .. } => {
82 let val = match value {
83 ScConst::I64(v) => format!("{v}"),
84 ScConst::U64(v) => format!("{v}"),
85 ScConst::F64(v) => format!("{}", (*v * 256.0) as i64),
86 _ => "0".to_string(),
87 };
88 mlir.push_str(&format!(" %c{} = hw.constant {} : i16\n", id.0, val));
89 }
90 ScOp::LifStep {
91 id,
92 current,
93 params,
94 ..
95 } => {
96 let cur = value_wire(graph, *current);
97 mlir.push_str(&format!(
98 " %v{id}_spike, %v{id}_v = hw.instance \"lif_{id}\" \
99 @sc_lif_neuron<DATA_WIDTH: i32 = {dw}, V_THRESHOLD: i32 = {vt}>(\
100 clk: %clk: i1, rst_n: %rst_n: i1, I_t: {cur}: i1) -> \
101 (spike_out: i1, v_out: i{dw})\n",
102 id = id.0,
103 dw = params.data_width,
104 vt = params.v_threshold,
105 cur = cur,
106 ));
107 last_output = format!("%v{}_spike", id.0);
108 }
109 ScOp::DenseForward { id, params, .. } => {
110 mlir.push_str(&format!(
111 " %v{} = hw.instance \"dense_{}\" @sc_dense_layer_core<\
112 N_INPUTS: i32 = {}, N_NEURONS: i32 = {}>(\
113 clk: %clk: i1, rst_n: %rst_n: i1) -> (spikes: i{})\n",
114 id.0, id.0, params.n_inputs, params.n_neurons, params.n_neurons
115 ));
116 last_output = format!("%v{}", id.0);
117 }
118 ScOp::Popcount { id, input } => {
119 let inp = value_wire(graph, *input);
120 mlir.push_str(&format!(" %v{} = comb.popcount {inp} : i64\n", id.0));
121 }
122 ScOp::GraphForward { id, features, .. } => {
123 let inp = value_wire(graph, *features);
124 mlir.push_str(&format!(
125 " // GraphForward: SC AND-popcount aggregation\n %v{} = {inp} : i64\n",
126 id.0
127 ));
128 }
129 ScOp::SoftmaxAttention { id, v, .. } => {
130 let inp = value_wire(graph, *v);
131 mlir.push_str(&format!(
132 " // SoftmaxAttention: SC bitstream attention\n %v{} = {inp} : i64\n",
133 id.0
134 ));
135 }
136 ScOp::KuramotoStep { id, phases, .. } => {
137 let inp = value_wire(graph, *phases);
138 mlir.push_str(&format!(
139 " // KuramotoStep: phase accumulator with coupling\n %v{} = {inp} : i64\n",
140 id.0
141 ));
142 }
143 ScOp::Scale { id, input, factor } => {
144 let inp = value_wire(graph, *input);
145 let scale_int = (*factor * 256.0) as i64;
146 mlir.push_str(&format!(
147 " %v{} = comb.mul {inp}, %c_scale_{id} : i16\n",
148 id.0,
149 inp = inp,
150 id = id.0,
151 ));
152 let _ = scale_int; }
154 ScOp::Offset { id, input, offset } => {
155 let inp = value_wire(graph, *input);
156 let _ = offset;
157 mlir.push_str(&format!(
158 " %v{} = comb.add {inp}, %c_off_{id} : i16\n",
159 id.0,
160 inp = inp,
161 id = id.0,
162 ));
163 }
164 ScOp::DivConst { id, input, divisor } => {
165 let inp = value_wire(graph, *input);
166 let _ = divisor;
167 mlir.push_str(&format!(
168 " %v{} = comb.divu {inp}, %c_div_{id} : i16\n",
169 id.0,
170 inp = inp,
171 id = id.0,
172 ));
173 }
174 ScOp::Reduce { id, input, mode } => {
175 let inp = value_wire(graph, *input);
176 let op_name = match mode {
177 ReduceMode::Sum => "add",
178 ReduceMode::Max => "max",
179 };
180 mlir.push_str(&format!(" %v{} = comb.{op_name} {inp} : i64\n", id.0,));
181 }
182 }
183 }
184
185 mlir.push_str("}\n");
187 let _ = last_output;
188 Ok(mlir)
189}
190
191fn value_wire(graph: &ScGraph, id: ValueId) -> String {
192 for op in &graph.ops {
193 if op.result_id() == id {
194 return match op {
195 ScOp::Input { name, .. } => format!("%{name}"),
196 ScOp::Constant { id, .. } => format!("%c{}", id.0),
197 ScOp::LifStep { id, .. } => format!("%v{}_spike", id.0),
198 ScOp::DenseForward { id, .. } => format!("%v{}", id.0),
199 _ => format!("%v{}", id.0),
200 };
201 }
202 }
203 format!("%v{}", id.0)
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use crate::ir::builder::ScGraphBuilder;
210
211 #[test]
212 fn basic_and_gate_emits_mlir() {
213 let mut b = ScGraphBuilder::new("test_and");
214 let a = b.input("a", ScType::Bool);
215 let bv = b.input("b", ScType::Bool);
216 let c = b.bitwise_and(a, bv);
217 b.output("out", c);
218 let graph = b.build();
219
220 let mlir = emit(&graph).unwrap();
221 assert!(mlir.contains("hw.module @test_and"));
222 assert!(mlir.contains("comb.and"));
223 assert!(mlir.contains("hw.output"));
224 }
225
226 #[test]
227 fn module_wrapping() {
228 let mut b = ScGraphBuilder::new("wrapper");
229 let x = b.input("x", ScType::Bool);
230 b.output("y", x);
231 let graph = b.build();
232
233 let mlir = emit(&graph).unwrap();
234 assert!(mlir.starts_with("// Auto-generated"));
235 assert!(mlir.contains("hw.module @wrapper"));
236 assert!(mlir.ends_with("}\n"));
237 }
238
239 #[test]
240 fn xor_gate() {
241 let mut b = ScGraphBuilder::new("test_xor");
242 let a = b.input("a", ScType::Bool);
243 let bv = b.input("b", ScType::Bool);
244 let c = b.bitwise_xor(a, bv);
245 b.output("out", c);
246 let graph = b.build();
247
248 let mlir = emit(&graph).unwrap();
249 assert!(mlir.contains("comb.xor"));
250 }
251}