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