logicaffeine_compile/codegen_sva/
terminal_rtl.rs1use super::terminal_protocol::fill_verilog;
8
9const UART: &str = include_str!("terminal/term_uart.v");
10const SLIP: &str = include_str!("terminal/term_slip.v");
11const PS2: &str = include_str!("terminal/term_ps2.v");
12const VGA: &str = include_str!("terminal/term_vga.v");
13const LINK: &str = include_str!("terminal/term_link.v");
14const ACTIVITY: &str = include_str!("terminal/term_activity.v");
15const TOP: &str = include_str!("terminal/term_top.v");
16const XDC: &str = include_str!("terminal/term_top.xdc");
17
18pub const FONT_HEX: &str = include_str!("terminal/term_font.hex");
20pub const FONT_LICENSE: &str = include_str!("terminal/term_font.LICENSE");
22
23pub fn modules(engine: bool) -> Vec<(&'static str, String)> {
26 let mut files = vec![
27 ("term_uart.v", fill_verilog(UART)),
28 ("term_slip.v", fill_verilog(SLIP)),
29 ("term_ps2.v", fill_verilog(PS2)),
30 ("term_vga.v", fill_verilog(VGA)),
31 ("term_link.v", fill_verilog(LINK)),
32 ];
33 if engine {
34 files.push(("term_activity.v", fill_verilog(ACTIVITY)));
35 }
36 files
37}
38
39fn select(text: &str, keep: &str, drop: &str) -> String {
42 let (kb, ke) = (format!("//__{keep}_BEGIN__\n"), format!("//__{keep}_END__\n"));
43 let (db, de) = (format!("//__{drop}_BEGIN__\n"), format!("//__{drop}_END__\n"));
44 let start = text.find(&db).unwrap_or_else(|| panic!("the terminal top has no {drop} section"));
45 let end = text[start..].find(&de).map(|e| start + e + de.len()).expect("an unterminated section");
46 let without = format!("{}{}", &text[..start], &text[end..]);
47 without.replace(&kb, "").replace(&ke, "")
48}
49
50pub fn top(top: &str, engine: Option<&str>) -> String {
54 let text = match engine {
55 Some(module) => select(TOP, "ENGINE", "NO_ENGINE").replace("__ENGINE_MODULE__", module),
56 None => select(TOP, "NO_ENGINE", "ENGINE"),
57 };
58 fill_verilog(&text.replace("__TOP__", top))
59}
60
61pub fn xdc() -> String {
63 XDC.to_string()
64}
65
66pub const ENGINE_MODULE: &str = "wpe_soc_fpga_probed";
68pub const ENGINE_FILE: &str = "nexys_fabric_terminal_engine.v";
70
71pub fn board_files() -> Vec<(String, String)> {
75 let mut out: Vec<(String, String)> = modules(true).into_iter().map(|(name, text)| (name.to_string(), text)).collect();
76 out.push(("term_font.hex".into(), FONT_HEX.into()));
77 out.push(("term_font8.hex".into(), font8().iter().map(|b| format!("{b:02x}\n")).collect()));
78 out.push(("term_font.LICENSE".into(), FONT_LICENSE.into()));
79 out.push(("nexys_fabric_terminal.xdc".into(), xdc()));
80 out.push((
81 ENGINE_FILE.to_string(),
82 super::wprog_engine_rtl::emit_wprog_engine_soc_verilog_hw_probed(
83 ENGINE_MODULE.strip_prefix("wpe_soc_").expect("an engine module is wpe_soc_<name>"),
84 ),
85 ));
86 out.push(("nexys_fabric_terminal.v".into(), top("nexys_fabric_terminal", Some(ENGINE_MODULE))));
87 out.push(("nexys_terminal_shell.v".into(), top("nexys_terminal_shell", None)));
88 out
89}
90
91pub fn font() -> Vec<u8> {
93 FONT_HEX.lines().map(|l| u8::from_str_radix(l.trim(), 16).expect("the font ROM is hex bytes")).collect()
94}
95
96pub fn font8() -> Vec<u8> {
99 let font = font();
100 (0..font.len() / 16)
101 .flat_map(|g| super::terminal_protocol::FONT8_ROWS.iter().map(move |&r| (g, r)))
102 .map(|(g, r)| font[g * 16 + r])
103 .collect()
104}
105
106#[derive(Clone, Debug)]
109pub struct Screen {
110 pub cells: Vec<super::terminal_protocol::Cell>,
112 pub small: Vec<super::terminal_protocol::Cell>,
114 pub window: Option<(u8, u8, u8, u8)>,
116 pub cursor: Option<(u8, u8)>,
117 pub pointer: Option<(u8, u8)>,
118 pub flash: bool,
119 pub panel: Option<Panel>,
120}
121
122impl Screen {
123 pub fn blank() -> Self {
125 use super::terminal_protocol::{Cell, COLS, ROWS, SMALL_COLS, SMALL_ROWS};
126 Screen {
127 cells: vec![Cell::new(b' ', 7, 0); ROWS as usize * COLS as usize],
128 small: vec![Cell::new(b' ', 7, 0); SMALL_ROWS as usize * SMALL_COLS as usize],
129 window: None,
130 cursor: None,
131 pointer: None,
132 flash: false,
133 panel: None,
134 }
135 }
136}
137
138#[derive(Clone, Debug, PartialEq, Eq)]
141pub struct Panel {
142 pub block_hits: Vec<u32>,
144 pub cell_hits: Vec<u32>,
146 pub running: bool,
147 pub pc: u16,
148 pub cell: u16,
149 pub sp: u16,
150 pub hp: u16,
151 pub cycles: u32,
152}
153
154impl Panel {
155 pub fn idle() -> Self {
157 use super::wprog_engine_rtl::{WPE_HW_MAXBLK, WPE_HW_MAXCELL};
158 Panel {
159 block_hits: vec![0; WPE_HW_MAXBLK],
160 cell_hits: vec![0; WPE_HW_MAXCELL],
161 running: false,
162 pc: 0,
163 cell: 0,
164 sp: 0,
165 hp: 0,
166 cycles: 0,
167 }
168 }
169
170 pub fn pixel(&self, font: &[u8], x: usize, y: usize) -> Option<u16> {
186 use super::terminal_protocol::{
187 heat_level, GLYPH_ROWS, HEAT, PANEL_BLOCK_COL, PANEL_BLOCK_ROW, PANEL_BLOCK_TILE, PANEL_CELL_COL,
188 PANEL_CELL_ROW, PANEL_CYCLES_COL, PANEL_DIGIT, PANEL_GAUGE_UNLIT, PANEL_HEAP_COL, PANEL_HEAP_LIT,
189 PANEL_OUTLINE, PANEL_STACK_COL, PANEL_STACK_LIT, PANEL_TITLE_ROW,
190 };
191 use super::wprog_engine_rtl::{WPE_HW_DEPTH, WPE_HW_HEAP};
192 let row = y / GLYPH_ROWS;
193 let gy = y % GLYPH_ROWS;
194 let span = |col: u8, width: usize| (x >= col as usize * 8 && x < col as usize * 8 + width).then(|| x - col as usize * 8);
195 let heat_box = |size: usize, hits: u32, current: bool, sub: usize| {
196 let inner = sub >= 2 && sub < size - 2 && (2..14).contains(&gy);
197 let rim = sub == 2 || sub == size - 3 || gy == 2 || gy == 13;
198 if !inner {
199 0
200 } else if current && rim {
201 PANEL_OUTLINE
202 } else {
203 HEAT[heat_level(hits)]
204 }
205 };
206 if row == PANEL_TITLE_ROW as usize {
207 let dx = span(PANEL_CYCLES_COL, 80)?;
208 let place = 9 - dx / 8;
209 let above = self.cycles as u64 / 10u64.pow(place as u32);
210 let digit = (above % 10) as u8;
211 let bits = font[(b'0' + digit) as usize * GLYPH_ROWS + gy];
212 let on = (above != 0 || place == 0) && (bits >> (7 - dx % 8)) & 1 == 1;
213 return Some(if on { PANEL_DIGIT } else { 0 });
214 }
215 if row == PANEL_BLOCK_ROW as usize {
216 let tile = PANEL_BLOCK_TILE as usize * 8;
217 if let Some(dx) = span(PANEL_BLOCK_COL, self.block_hits.len() * tile) {
218 let b = dx / tile;
219 return Some(heat_box(tile, self.block_hits[b], self.running && self.pc as usize == b, dx % tile));
220 }
221 if let Some(dx) = span(PANEL_STACK_COL, WPE_HW_DEPTH * 8) {
222 let inner = (1..7).contains(&(dx % 8)) && (3..13).contains(&gy);
223 let lit = dx / 8 < self.sp as usize;
224 return Some(if !inner { 0 } else if lit { PANEL_STACK_LIT } else { PANEL_GAUGE_UNLIT });
225 }
226 if let Some(dx) = span(PANEL_HEAP_COL, WPE_HW_HEAP) {
227 let inner = (4..12).contains(&gy);
228 let lit = dx < self.hp as usize;
229 return Some(if !inner { 0 } else if lit { PANEL_HEAP_LIT } else { PANEL_GAUGE_UNLIT });
230 }
231 return None;
232 }
233 if row == PANEL_CELL_ROW as usize {
234 let dx = span(PANEL_CELL_COL, self.cell_hits.len() * 16)?;
235 let c = dx / 16;
236 return Some(heat_box(16, self.cell_hits[c], self.running && self.cell as usize == c, dx % 16));
237 }
238 None
239 }
240}
241
242pub fn render(screen: &Screen) -> Vec<u16> {
246 use super::terminal_protocol::{COLS, GLYPH_ROWS, H_TIMING, PALETTE, SMALL_COLS, V_TIMING};
247 let (font, font8) = (font(), font8());
248 let (w, h) = (H_TIMING.0 as usize, V_TIMING.0 as usize);
249 let mut out = Vec::with_capacity(w * h);
250 for y in 0..h {
251 for x in 0..w {
252 let (row, col) = ((y / GLYPH_ROWS) as u8, (x / 8) as u8);
253 let in_window = screen
254 .window
255 .filter(|&(r, c, rs, cs)| rs > 0 && (r..r + rs).contains(&row) && (c..c + cs).contains(&col));
256 let (cell, bits) = match in_window {
257 Some((r, c, _, _)) => {
258 let srow = (row - r) as usize * 2 + (y % GLYPH_ROWS) / 8;
259 let cell = screen.small[srow * SMALL_COLS as usize + (col - c) as usize];
260 (cell, font8[cell.glyph as usize * 8 + y % 8])
261 }
262 None => {
263 let cell = screen.cells[row as usize * COLS as usize + col as usize];
264 (cell, font[cell.glyph as usize * GLYPH_ROWS + y % GLYPH_ROWS])
265 }
266 };
267 let on = (bits >> (7 - x % 8)) & 1 == 1 && !(cell.blink && !screen.flash);
268 let underline = screen.cursor == Some((row, col)) && screen.flash && y % GLYPH_ROWS >= 14;
269 let lit = (on || underline) ^ (screen.pointer == Some((row, col)));
270 let text = PALETTE[if lit { cell.fg } else { cell.bg } as usize];
271 out.push(screen.panel.as_ref().and_then(|p| p.pixel(&font, x, y)).unwrap_or(text));
272 }
273 }
274 out
275}