Skip to main content

veryl_analyzer/ir/
system_function.rs

1use crate::conv::Context;
2use crate::ir::assign_table::{AssignContext, AssignTable};
3use crate::ir::ff_table::FfTable;
4use crate::ir::{
5    AssignDestination, Comptime, Expression, IrResult, Shape, Type, TypeKind, ValueVariant,
6    VarPathSelect,
7};
8use crate::value::Value;
9use crate::{AnalyzerError, BigUint, ir_error};
10use std::fmt;
11use veryl_parser::resource_table::StrId;
12use veryl_parser::token_range::TokenRange;
13
14#[derive(Clone, Debug)]
15pub struct Input(pub Expression);
16
17impl fmt::Display for Input {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        self.0.fmt(f)
20    }
21}
22
23#[derive(Clone, Debug)]
24pub struct Output(pub Vec<AssignDestination>);
25
26impl fmt::Display for Output {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        let mut ret = String::new();
29
30        if self.0.len() == 1 {
31            ret.push_str(&format!("{}", self.0[0]));
32        } else if !self.0.is_empty() {
33            ret.push_str(&format!("{{{}", self.0[0]));
34            for d in &self.0[1..] {
35                ret.push_str(&format!(", {}", d));
36            }
37            ret.push_str("}}");
38        }
39
40        ret.fmt(f)
41    }
42}
43
44#[derive(Clone, Debug)]
45pub struct SystemFunctionCall {
46    pub kind: SystemFunctionKind,
47    pub comptime: Comptime,
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq)]
51pub enum AssertKind {
52    Fatal,
53    Continue,
54}
55
56impl AssertKind {
57    pub fn as_str(&self) -> &'static str {
58        match self {
59            AssertKind::Fatal => "$assert",
60            AssertKind::Continue => "$assert_continue",
61        }
62    }
63}
64
65#[derive(Clone, Debug)]
66pub enum SystemFunctionKind {
67    Bits(Input),
68    Size(Input),
69    Clog2(Input),
70    Onehot(Input),
71    Readmemh(Input, Output),
72    Display(Vec<Input>),
73    Write(Vec<Input>),
74    Assert {
75        kind: AssertKind,
76        cond: Input,
77        args: Vec<Input>,
78    },
79    Finish,
80    Signed(Input),
81    Unsigned(Input),
82}
83
84fn create_input(
85    context: &mut Context,
86    name: StrId,
87    r#type: Option<Type>,
88    arg: (Expression, Vec<VarPathSelect>, TokenRange),
89) -> Input {
90    let (mut expr, _, token) = arg;
91
92    // eval_comptime is required for width propagation, not just the type check.
93    let comptime = expr.eval_comptime(context, None);
94    if let Some(r#type) = r#type
95        && !r#type.compatible(comptime)
96    {
97        context.insert_error(AnalyzerError::mismatch_function_arg(
98            &name.to_string(),
99            &comptime.r#type.to_string(),
100            &token,
101        ));
102    }
103
104    Input(expr)
105}
106
107fn create_output(
108    context: &mut Context,
109    name: StrId,
110    r#type: Option<Type>,
111    arg: (Expression, Vec<VarPathSelect>, TokenRange),
112) -> Output {
113    let (mut expr, dst, token) = arg;
114
115    let dst = dst
116        .into_iter()
117        .filter_map(|x| x.to_assign_destination(context, false))
118        .collect();
119
120    if let Some(r#type) = r#type {
121        let comptime = expr.eval_comptime(context, None);
122        if !r#type.compatible(comptime) {
123            context.insert_error(AnalyzerError::mismatch_function_arg(
124                &name.to_string(),
125                &comptime.r#type.to_string(),
126                &token,
127            ));
128        }
129    }
130
131    Output(dst)
132}
133
134impl SystemFunctionCall {
135    pub fn new(
136        context: &mut Context,
137        name: StrId,
138        mut args: Vec<(Expression, Vec<VarPathSelect>, TokenRange)>,
139        token: TokenRange,
140    ) -> IrResult<Self> {
141        let mut comptime = Comptime::create_unknown(token);
142        comptime.is_const = true;
143
144        match name.to_string().as_str() {
145            "$bits" => {
146                if args.len() != 1 {
147                    context.insert_error(AnalyzerError::mismatch_function_arity(
148                        "$bits",
149                        1,
150                        args.len(),
151                        &token,
152                    ));
153                    return Err(ir_error!(token));
154                }
155                let arg0 = create_input(context, name, None, args.remove(0));
156                Ok(SystemFunctionCall {
157                    kind: SystemFunctionKind::Bits(arg0),
158                    comptime,
159                })
160            }
161            "$size" => {
162                if args.len() != 1 {
163                    context.insert_error(AnalyzerError::mismatch_function_arity(
164                        "$size",
165                        1,
166                        args.len(),
167                        &token,
168                    ));
169                    return Err(ir_error!(token));
170                }
171                let arg0 = create_input(context, name, None, args.remove(0));
172                Ok(SystemFunctionCall {
173                    kind: SystemFunctionKind::Size(arg0),
174                    comptime,
175                })
176            }
177            "$clog2" => {
178                if args.len() != 1 {
179                    context.insert_error(AnalyzerError::mismatch_function_arity(
180                        "$clog2",
181                        1,
182                        args.len(),
183                        &token,
184                    ));
185                    return Err(ir_error!(token));
186                }
187                let arg0_type = {
188                    let mut t = Type::new(TypeKind::Logic);
189                    t.set_concrete_width(Shape::new(vec![Some(32)]));
190                    t
191                };
192                let arg0 = create_input(context, name, Some(arg0_type), args.remove(0));
193                Ok(SystemFunctionCall {
194                    kind: SystemFunctionKind::Clog2(arg0),
195                    comptime,
196                })
197            }
198            "$onehot" => {
199                if args.len() != 1 {
200                    context.insert_error(AnalyzerError::mismatch_function_arity(
201                        "$onehot",
202                        1,
203                        args.len(),
204                        &token,
205                    ));
206                    return Err(ir_error!(token));
207                }
208                let arg0_type = {
209                    let mut t = Type::new(TypeKind::Logic);
210                    t.set_concrete_width(Shape::new(vec![Some(32)]));
211                    t
212                };
213                let arg0 = create_input(context, name, Some(arg0_type), args.remove(0));
214                Ok(SystemFunctionCall {
215                    kind: SystemFunctionKind::Onehot(arg0),
216                    comptime,
217                })
218            }
219            "$readmemh" => {
220                if args.len() != 2 {
221                    context.insert_error(AnalyzerError::mismatch_function_arity(
222                        "$readmemh",
223                        2,
224                        args.len(),
225                        &token,
226                    ));
227                    return Err(ir_error!(token));
228                }
229                let arg0 = create_input(context, name, None, args.remove(0));
230                let arg1 = create_output(context, name, None, args.remove(0));
231                Ok(SystemFunctionCall {
232                    kind: SystemFunctionKind::Readmemh(arg0, arg1),
233                    comptime,
234                })
235            }
236            "$display" => {
237                let inputs: Vec<Input> = args
238                    .into_iter()
239                    .map(|arg| create_input(context, name, None, arg))
240                    .collect();
241                Ok(SystemFunctionCall {
242                    kind: SystemFunctionKind::Display(inputs),
243                    comptime,
244                })
245            }
246            "$write" => {
247                let inputs: Vec<Input> = args
248                    .into_iter()
249                    .map(|arg| create_input(context, name, None, arg))
250                    .collect();
251                Ok(SystemFunctionCall {
252                    kind: SystemFunctionKind::Write(inputs),
253                    comptime,
254                })
255            }
256            "$assert" | "$assert_continue" => {
257                if args.is_empty() {
258                    context.insert_error(AnalyzerError::mismatch_function_arity(
259                        &name.to_string(),
260                        1,
261                        0,
262                        &token,
263                    ));
264                    return Err(ir_error!(token));
265                }
266                let kind = if name.to_string() == "$assert_continue" {
267                    AssertKind::Continue
268                } else {
269                    AssertKind::Fatal
270                };
271                let cond = create_input(context, name, None, args.remove(0));
272                let args: Vec<Input> = args
273                    .into_iter()
274                    .map(|arg| create_input(context, name, None, arg))
275                    .collect();
276                Ok(SystemFunctionCall {
277                    kind: SystemFunctionKind::Assert { kind, cond, args },
278                    comptime,
279                })
280            }
281            "$finish" => {
282                if !args.is_empty() {
283                    return Err(ir_error!(token));
284                }
285                Ok(SystemFunctionCall {
286                    kind: SystemFunctionKind::Finish,
287                    comptime,
288                })
289            }
290            "$signed" => {
291                if args.len() != 1 {
292                    return Err(ir_error!(token));
293                }
294
295                let mut arg = args.remove(0);
296                arg.0.eval_comptime(context, None);
297
298                let arg0 = create_input(context, name, None, arg);
299                comptime.expr_context.signed = true;
300
301                Ok(SystemFunctionCall {
302                    kind: SystemFunctionKind::Signed(arg0),
303                    comptime,
304                })
305            }
306            "$unsigned" => {
307                if args.len() != 1 {
308                    return Err(ir_error!(token));
309                }
310
311                let mut arg = args.remove(0);
312                arg.0.eval_comptime(context, None);
313
314                let arg0 = create_input(context, name, None, arg);
315                comptime.expr_context.signed = false;
316
317                Ok(SystemFunctionCall {
318                    kind: SystemFunctionKind::Unsigned(arg0),
319                    comptime,
320                })
321            }
322            _ => Err(ir_error!(token)),
323        }
324    }
325
326    pub fn eval_value(&self, context: &mut Context) -> Option<Value> {
327        match &self.kind {
328            SystemFunctionKind::Bits(x) => {
329                let mut expr = x.0.clone();
330                let comptime = expr.eval_comptime(context, None);
331                let value = match &comptime.value {
332                    ValueVariant::Numeric(_) => comptime.r#type.total_width(),
333                    ValueVariant::Type(x) => x.total_width(),
334                    _ => None,
335                };
336                value.map(|x| Value::new(x as u64, 32, false))
337            }
338            SystemFunctionKind::Size(x) => {
339                let mut expr = x.0.clone();
340                let comptime = expr.eval_comptime(context, None);
341                let value = match &comptime.value {
342                    ValueVariant::Numeric(_) => comptime.r#type.total_width(),
343                    ValueVariant::Type(x) => x.total_width(),
344                    _ => None,
345                };
346                value.map(|x| Value::new(x as u64, 32, false))
347            }
348            SystemFunctionKind::Clog2(x) => {
349                let value = x.0.eval_value(context)?;
350                let value = value.payload();
351                let ret = if value.as_ref() == &BigUint::from(0u32) {
352                    BigUint::from(0u32)
353                } else {
354                    value.as_ref() - BigUint::from(1u32)
355                };
356                Some(Value::new(ret.bits(), 32, false))
357            }
358            SystemFunctionKind::Onehot(x) => {
359                let value = x.0.eval_value(context)?;
360                let ret = value.payload().count_ones() == 1;
361                Some(Value::new(ret.into(), 1, false))
362            }
363            SystemFunctionKind::Readmemh(_, _) => None,
364            SystemFunctionKind::Display(_) => None,
365            SystemFunctionKind::Write(_) => None,
366            SystemFunctionKind::Assert { .. } => None,
367            SystemFunctionKind::Finish => None,
368            SystemFunctionKind::Signed(x) | SystemFunctionKind::Unsigned(x) => {
369                x.0.eval_value(context)
370            }
371        }
372    }
373
374    pub fn eval_comptime(&self, context: &mut Context) -> Comptime {
375        let value = self.eval_value(context);
376        match &self.kind {
377            SystemFunctionKind::Bits(_)
378            | SystemFunctionKind::Size(_)
379            | SystemFunctionKind::Clog2(_)
380            | SystemFunctionKind::Onehot(_) => {
381                let mut ret = self.comptime.clone();
382                if let Some(x) = value {
383                    ret.value = ValueVariant::Numeric(x);
384                }
385                ret
386            }
387            SystemFunctionKind::Readmemh(_, _) => self.comptime.clone(),
388            SystemFunctionKind::Display(_) => self.comptime.clone(),
389            SystemFunctionKind::Write(_) => self.comptime.clone(),
390            SystemFunctionKind::Assert { .. } => self.comptime.clone(),
391            SystemFunctionKind::Finish => self.comptime.clone(),
392            SystemFunctionKind::Signed(_) | SystemFunctionKind::Unsigned(_) => {
393                let mut ret = self.comptime.clone();
394                if let Some(x) = value {
395                    ret.value = ValueVariant::Numeric(x);
396                }
397                ret
398            }
399        }
400    }
401
402    pub fn eval_assign(
403        &self,
404        context: &mut Context,
405        assign_table: &mut AssignTable,
406        assign_context: AssignContext,
407    ) {
408        if let SystemFunctionKind::Readmemh(_, x) = &self.kind {
409            for x in &x.0 {
410                x.eval_assign(context, assign_table, assign_context);
411            }
412        }
413    }
414}
415
416impl fmt::Display for SystemFunctionCall {
417    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418        match &self.kind {
419            SystemFunctionKind::Bits(x) => format!("$bits({x})").fmt(f),
420            SystemFunctionKind::Size(x) => format!("$size({x})").fmt(f),
421            SystemFunctionKind::Clog2(x) => format!("$clog2({x})").fmt(f),
422            SystemFunctionKind::Onehot(x) => format!("$onehot({x})").fmt(f),
423            SystemFunctionKind::Readmemh(x, y) => format!("$readmemh({x}, {y})").fmt(f),
424            SystemFunctionKind::Display(args) => {
425                let args_str: Vec<_> = args.iter().map(|a| format!("{a}")).collect();
426                format!("$display({})", args_str.join(", ")).fmt(f)
427            }
428            SystemFunctionKind::Write(args) => {
429                let args_str: Vec<_> = args.iter().map(|a| format!("{a}")).collect();
430                format!("$write({})", args_str.join(", ")).fmt(f)
431            }
432            SystemFunctionKind::Assert { kind, cond, args } => {
433                let name = kind.as_str();
434                if args.is_empty() {
435                    format!("{name}({cond})").fmt(f)
436                } else {
437                    let args_str: Vec<_> = args.iter().map(|a| format!("{a}")).collect();
438                    format!("{name}({cond}, {})", args_str.join(", ")).fmt(f)
439                }
440            }
441            SystemFunctionKind::Finish => "$finish()".fmt(f),
442            SystemFunctionKind::Signed(x) => format!("$signed({x})").fmt(f),
443            SystemFunctionKind::Unsigned(x) => format!("$unsigned({x})").fmt(f),
444        }
445    }
446}
447
448impl SystemFunctionCall {
449    pub fn gather_ff(
450        &self,
451        context: &mut Context,
452        table: &mut FfTable,
453        decl: usize,
454        from_ff: bool,
455    ) {
456        let process_input = |input: &Input, context: &mut Context, table: &mut FfTable| {
457            input.0.gather_ff(context, table, decl, None, from_ff);
458        };
459        match &self.kind {
460            SystemFunctionKind::Bits(x)
461            | SystemFunctionKind::Size(x)
462            | SystemFunctionKind::Clog2(x)
463            | SystemFunctionKind::Onehot(x)
464            | SystemFunctionKind::Signed(x)
465            | SystemFunctionKind::Unsigned(x) => {
466                process_input(x, context, table);
467            }
468            SystemFunctionKind::Readmemh(_, _) => {
469                // First arg is filename literal, second is output array
470                // (written to, so handled via assignment, not reference).
471            }
472            SystemFunctionKind::Display(args) | SystemFunctionKind::Write(args) => {
473                for arg in args {
474                    process_input(arg, context, table);
475                }
476            }
477            SystemFunctionKind::Assert { cond, args, .. } => {
478                process_input(cond, context, table);
479                for arg in args {
480                    process_input(arg, context, table);
481                }
482            }
483            SystemFunctionKind::Finish => {}
484        }
485    }
486}