logicaffeine_compile/codegen_sva/
chip_protocol.rs1pub const IMAGE_HEAP: u8 = 0x48;
25pub const IMAGE_GO: u8 = 0x5A;
27
28pub const REC_SHOW: u8 = 0xA7;
30pub const REC_CALL: u8 = 0xA8;
32pub const REC_REPLY: u8 = 0xA9;
34
35pub const FRAME_SYNC: u8 = 0xA5;
37pub const FRAME_END: u8 = 0x5A;
39pub const FRAME_END_CALL_STACK: u8 = 0xE1;
41pub const FRAME_END_HEAP: u8 = 0xE2;
43
44pub const TAG_INT: u8 = 0;
46pub const TAG_FLOAT: u8 = 1;
48pub const TAG_TEXT: u8 = 2;
51pub const TAG_WORD32: u8 = 3;
53pub const TAG_BOOL: u8 = 4;
55
56pub const TAGS: [u8; 5] = [TAG_INT, TAG_FLOAT, TAG_TEXT, TAG_WORD32, TAG_BOOL];
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
62#[repr(u8)]
63pub enum HostCall {
64 Listen = 1,
66 Connect = 2,
68 Send = 3,
70 Await = 4,
72 Stream = 5,
74 AwaitStream = 6,
76 Sync = 7,
78}
79
80impl HostCall {
81 pub const ALL: [HostCall; 7] = [
83 HostCall::Listen,
84 HostCall::Connect,
85 HostCall::Send,
86 HostCall::Await,
87 HostCall::Stream,
88 HostCall::AwaitStream,
89 HostCall::Sync,
90 ];
91
92 pub fn code(self) -> u8 {
94 self as u8
95 }
96
97 pub fn from_code(code: u8) -> Option<HostCall> {
99 HostCall::ALL.into_iter().find(|c| c.code() == code)
100 }
101
102 pub fn name(self) -> &'static str {
104 match self {
105 HostCall::Listen => "listen",
106 HostCall::Connect => "connect",
107 HostCall::Send => "send",
108 HostCall::Await => "await",
109 HostCall::Stream => "stream",
110 HostCall::AwaitStream => "await_stream",
111 HostCall::Sync => "sync",
112 }
113 }
114}
115
116#[derive(Clone, Debug, PartialEq)]
118pub enum ChipValue {
119 Int(i64),
120 Float(u64),
122 Text(String),
123 Word32(u32),
124 Bool(bool),
125}
126
127impl ChipValue {
128 pub fn tag(&self) -> u8 {
130 match self {
131 ChipValue::Int(_) => TAG_INT,
132 ChipValue::Float(_) => TAG_FLOAT,
133 ChipValue::Text(_) => TAG_TEXT,
134 ChipValue::Word32(_) => TAG_WORD32,
135 ChipValue::Bool(_) => TAG_BOOL,
136 }
137 }
138
139 pub fn encode(&self, out: &mut Vec<u8>) {
141 out.push(self.tag());
142 match self {
143 ChipValue::Int(v) => out.extend_from_slice(&v.to_be_bytes()),
144 ChipValue::Float(bits) => out.extend_from_slice(&bits.to_be_bytes()),
145 ChipValue::Word32(w) => out.extend_from_slice(&u64::from(*w).to_be_bytes()),
146 ChipValue::Bool(b) => out.extend_from_slice(&u64::from(*b).to_be_bytes()),
147 ChipValue::Text(s) => {
148 out.extend_from_slice(&(s.len() as u32).to_be_bytes());
149 out.extend_from_slice(s.as_bytes());
150 }
151 }
152 }
153
154 pub fn decode(bytes: &[u8]) -> Result<(ChipValue, usize), FrameError> {
156 let tag = *bytes.first().ok_or(FrameError::Short)?;
157 let word = |at: usize| -> Result<u64, FrameError> {
158 let b: [u8; 8] = bytes
159 .get(at..at + 8)
160 .ok_or(FrameError::Short)?
161 .try_into()
162 .expect("8 bytes");
163 Ok(u64::from_be_bytes(b))
164 };
165 match tag {
166 TAG_INT => Ok((ChipValue::Int(word(1)? as i64), 9)),
167 TAG_FLOAT => Ok((ChipValue::Float(word(1)?), 9)),
168 TAG_WORD32 => {
169 let w = word(1)?;
170 let w = u32::try_from(w)
171 .map_err(|_| FrameError::Malformed(format!("a Word32 of {w:#x}")))?;
172 Ok((ChipValue::Word32(w), 9))
173 }
174 TAG_BOOL => match word(1)? {
175 0 => Ok((ChipValue::Bool(false), 9)),
176 1 => Ok((ChipValue::Bool(true), 9)),
177 w => Err(FrameError::Malformed(format!("a Bool of {w}"))),
178 },
179 TAG_TEXT => {
180 let len: [u8; 4] = bytes
181 .get(1..5)
182 .ok_or(FrameError::Short)?
183 .try_into()
184 .expect("4 bytes");
185 let len = u32::from_be_bytes(len) as usize;
186 let text = bytes.get(5..5 + len).ok_or(FrameError::Short)?;
187 let text = String::from_utf8(text.to_vec())
188 .map_err(|_| FrameError::Malformed("a Text that is not UTF-8".to_string()))?;
189 Ok((ChipValue::Text(text), 5 + len))
190 }
191 t => Err(FrameError::Malformed(format!("value tag {t}"))),
192 }
193 }
194}
195
196#[derive(Clone, Debug, PartialEq)]
198pub enum Record {
199 Show(ChipValue),
200 Call {
201 call: HostCall,
202 args: Vec<ChipValue>,
203 },
204 Reply(ChipValue),
205}
206
207#[derive(Clone, Debug, PartialEq, Eq)]
209pub enum FrameError {
210 Short,
212 Malformed(String),
214}
215
216impl Record {
217 pub fn encode(&self) -> Vec<u8> {
219 let mut out = Vec::new();
220 match self {
221 Record::Show(v) => {
222 out.push(REC_SHOW);
223 v.encode(&mut out);
224 }
225 Record::Call { call, args } => {
226 out.push(REC_CALL);
227 out.push(call.code());
228 out.push(
229 u8::try_from(args.len()).expect("a host call carries at most 255 arguments"),
230 );
231 for a in args {
232 a.encode(&mut out);
233 }
234 }
235 Record::Reply(v) => {
236 out.push(REC_REPLY);
237 v.encode(&mut out);
238 }
239 }
240 out
241 }
242
243 pub fn decode(bytes: &[u8]) -> Result<(Record, usize), FrameError> {
245 let marker = *bytes.first().ok_or(FrameError::Short)?;
246 match marker {
247 REC_SHOW => {
248 let (v, n) = ChipValue::decode(&bytes[1..])?;
249 Ok((Record::Show(v), 1 + n))
250 }
251 REC_REPLY => {
252 let (v, n) = ChipValue::decode(&bytes[1..])?;
253 Ok((Record::Reply(v), 1 + n))
254 }
255 REC_CALL => {
256 let code = *bytes.get(1).ok_or(FrameError::Short)?;
257 let call = HostCall::from_code(code)
258 .ok_or_else(|| FrameError::Malformed(format!("host call {code}")))?;
259 let argc = *bytes.get(2).ok_or(FrameError::Short)? as usize;
260 let mut at = 3;
261 let mut args = Vec::with_capacity(argc);
262 for _ in 0..argc {
263 let (v, n) = ChipValue::decode(&bytes[at..])?;
264 args.push(v);
265 at += n;
266 }
267 Ok((Record::Call { call, args }, at))
268 }
269 m => Err(FrameError::Malformed(format!("record marker {m:#04x}"))),
270 }
271 }
272}
273
274#[derive(Clone, Copy, Debug, PartialEq, Eq)]
276pub struct ResultFrame {
277 pub result: i64,
278 pub cycles: u32,
279 pub end: u8,
281}
282
283impl ResultFrame {
284 pub fn encode(&self) -> Vec<u8> {
286 let mut out = vec![FRAME_SYNC];
287 out.extend_from_slice(&self.result.to_be_bytes());
288 out.extend_from_slice(&self.cycles.to_be_bytes());
289 out.push(self.end);
290 out
291 }
292
293 pub fn decode(bytes: &[u8]) -> Result<(ResultFrame, usize), FrameError> {
295 let sync = *bytes.first().ok_or(FrameError::Short)?;
296 if sync != FRAME_SYNC {
297 return Err(FrameError::Malformed(format!("frame sync {sync:#04x}")));
298 }
299 let body = bytes.get(1..14).ok_or(FrameError::Short)?;
300 let result = i64::from_be_bytes(body[0..8].try_into().expect("8 bytes"));
301 let cycles = u32::from_be_bytes(body[8..12].try_into().expect("4 bytes"));
302 let end = body[12];
303 if ![FRAME_END, FRAME_END_CALL_STACK, FRAME_END_HEAP].contains(&end) {
304 return Err(FrameError::Malformed(format!("frame end {end:#04x}")));
305 }
306 Ok((
307 ResultFrame {
308 result,
309 cycles,
310 end,
311 },
312 14,
313 ))
314 }
315}
316
317const VERILOG_BYTES: [(&str, u8); 8] = [
319 ("IMAGE_HEAP", IMAGE_HEAP),
320 ("IMAGE_GO", IMAGE_GO),
321 ("REC_SHOW", REC_SHOW),
322 ("REC_CALL", REC_CALL),
323 ("FRAME_SYNC", FRAME_SYNC),
324 ("FRAME_END", FRAME_END),
325 ("FRAME_END_CALL_STACK", FRAME_END_CALL_STACK),
326 ("FRAME_END_HEAP", FRAME_END_HEAP),
327];
328
329pub fn fill_verilog(template: &str) -> String {
332 let mut out = template.to_string();
333 for (name, byte) in VERILOG_BYTES {
334 out = out.replace(&format!("__V_{name}__"), &format!("8'h{byte:02X}"));
335 out = out.replace(&format!("__X_{name}__"), &format!("0x{byte:02X}"));
336 }
337 if let Some(at) = out.find("__V_").or_else(|| out.find("__X_")) {
338 let name: String = out[at..]
339 .chars()
340 .take_while(|c| !c.is_whitespace())
341 .collect();
342 panic!("the Verilog template names a protocol byte chip_protocol does not define: {name}");
343 }
344 out
345}
346
347pub fn table_json() -> String {
350 let tags = [
351 ("int", TAG_INT),
352 ("float", TAG_FLOAT),
353 ("text", TAG_TEXT),
354 ("word32", TAG_WORD32),
355 ("bool", TAG_BOOL),
356 ];
357 let tags: Vec<String> = tags.iter().map(|(n, v)| format!("\"{n}\": {v}")).collect();
358 let calls: Vec<String> = HostCall::ALL
359 .iter()
360 .map(|c| format!("\"{}\": {}", c.name(), c.code()))
361 .collect();
362 format!(
363 "{{\"image\": {{\"heap\": {IMAGE_HEAP}, \"go\": {IMAGE_GO}}}, \
364 \"records\": {{\"show\": {REC_SHOW}, \"call\": {REC_CALL}, \"reply\": {REC_REPLY}}}, \
365 \"frame\": {{\"sync\": {FRAME_SYNC}, \"end\": {FRAME_END}, \"end_call_stack\": {FRAME_END_CALL_STACK}, \
366 \"end_heap\": {FRAME_END_HEAP}}}, \"tags\": {{{}}}, \"calls\": {{{}}}}}",
367 tags.join(", "),
368 calls.join(", ")
369 )
370}