mister_fpga/
framebuffer.rs1use bitfield::bitfield;
2use image::{DynamicImage, RgbImage};
3use simple_endian::BigEndian;
4use tracing::debug;
5
6use cyclone_v::memory::{DevMemMemoryMapper, MemoryMapper};
7
8pub const FB_BASE_ADDRESS: usize = 0x2000_0000;
9pub const BUFFER_SIZE: usize = 2048 * 1024 * 3 * 4;
10
11pub const SCALER_FB_TYPE: u8 = 0x01;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14#[repr(u8)]
15pub enum ScalerPixelFormat {
16 RGB16 = 0,
18
19 RGB24 = 1,
21
22 RGBA32 = 2,
25
26 INVALID = 0xFF,
27}
28
29impl From<u8> for ScalerPixelFormat {
30 fn from(value: u8) -> Self {
31 match value {
32 0 => ScalerPixelFormat::RGB16,
33 1 => ScalerPixelFormat::RGB24,
34 2 => ScalerPixelFormat::RGBA32,
35 _ => ScalerPixelFormat::INVALID,
36 }
37 }
38}
39
40bitfield! {
41 #[derive(Clone, Copy, PartialEq, Eq)]
42 pub struct ScalerAttributes(u16);
43 impl Debug;
44 u8;
45
46 pub interlaced, _: 0;
47 pub field_number, _: 1;
48 pub horizontal_downscaled, _: 2;
49 pub vertical_downscaled, _: 3;
50
51 pub triple_buffered, _: 4;
53
54 pub frame_counter, _: 5, 3;
58}
59
60impl From<u16> for ScalerAttributes {
61 fn from(value: u16) -> Self {
62 ScalerAttributes(value)
63 }
64}
65
66#[derive(Debug, Copy, Clone)]
68#[repr(C)]
69pub(crate) struct FbHeader {
70 ty: u8,
71 scaler_pixel_format: u8,
72 header_len: BigEndian<u16>,
73 attributes: BigEndian<u16>,
74 width: BigEndian<u16>,
75 height: BigEndian<u16>,
76 line: BigEndian<u16>,
77 output_width: BigEndian<u16>,
78 output_height: BigEndian<u16>,
79}
80
81impl FbHeader {
82 #[inline]
83 pub unsafe fn from_memory(memory: *const u8) -> Option<Self> {
84 let header = (memory as *const FbHeader).read_volatile();
85 if header.ty == SCALER_FB_TYPE {
86 Some(header)
87 } else {
88 None
89 }
90 }
91
92 #[allow(unused)]
93 pub fn frame_checksum(&self) -> u8 {
94 self.attributes().frame_counter()
95 }
96
97 #[inline]
98 pub fn scaler_pixel_format(&self) -> ScalerPixelFormat {
99 self.scaler_pixel_format.into()
100 }
101
102 #[inline]
103 pub fn header_len(&self) -> u16 {
104 self.header_len.into()
105 }
106
107 #[inline]
108 pub fn attributes(&self) -> ScalerAttributes {
109 let bytes: u16 = self.attributes.into();
110 ScalerAttributes::from(bytes)
111 }
112
113 #[inline]
114 pub fn width(&self) -> u16 {
115 self.width.into()
116 }
117
118 #[inline]
119 pub fn height(&self) -> u16 {
120 self.height.into()
121 }
122
123 #[inline]
124 pub fn line(&self) -> u16 {
125 self.line.into()
126 }
127
128 #[allow(unused)]
129 pub fn output_width(&self) -> u16 {
130 self.output_width.into()
131 }
132
133 #[allow(unused)]
134 pub fn output_height(&self) -> u16 {
135 self.output_height.into()
136 }
137}
138
139#[derive(Debug, Clone, Copy)]
140pub enum FramebufferType {
141 Single,
142 TripleSmall,
143 TripleLarge,
144}
145
146impl FramebufferType {
147 #[inline]
148 pub(crate) fn offset_of(&self, index: u8) -> Option<usize> {
149 match (self, index) {
150 (_, 0) => Some(0),
151 (FramebufferType::TripleSmall, 1) => Some(0x0020_0000),
152 (FramebufferType::TripleSmall, 2) => Some(0x0040_0000),
153 (FramebufferType::TripleLarge, 1) => Some(0x0080_0000),
154 (FramebufferType::TripleLarge, 2) => Some(0x0100_0000),
155 _ => None,
156 }
157 }
158}
159
160pub struct FrameIter {
162 frame_counters: [*const u8; 3],
163}
164
165impl FrameIter {
166 pub fn new<M: MemoryMapper>(framebuffer: &FpgaFramebuffer<M>) -> Self {
167 let header0 = framebuffer.offset_of(0).unwrap();
168 let header1 = framebuffer.offset_of(1).unwrap_or(header0);
169 let header2 = framebuffer.offset_of(2).unwrap_or(header0);
170
171 unsafe {
172 let ptr0 = framebuffer.memory.as_ptr::<u8>().add(header0).add(5);
173 let ptr1 = framebuffer.memory.as_ptr::<u8>().add(header1).add(5);
174 let ptr2 = framebuffer.memory.as_ptr::<u8>().add(header2).add(5);
175
176 let frame_counters = [ptr0, ptr1, ptr2];
177
178 Self { frame_counters }
179 }
180 }
181}
182
183impl Iterator for FrameIter {
184 type Item = ();
185
186 fn next(&mut self) -> Option<Self::Item> {
187 unsafe {
188 let last: u8 = self.frame_counters.iter().map(|f| f.read_volatile()).sum();
189
190 loop {
191 let current: u8 = self.frame_counters.iter().map(|f| f.read_volatile()).sum();
192 if current != last {
193 break;
194 }
195 }
196
197 Some(())
198 }
199 }
200}
201
202#[derive(Debug, Clone, Copy)]
203pub struct FpgaFramebuffer<M: MemoryMapper> {
204 memory: M,
205
206 ty_: Option<FramebufferType>,
207}
208
209impl Default for FpgaFramebuffer<DevMemMemoryMapper> {
210 fn default() -> Self {
211 let address = FB_BASE_ADDRESS;
215 let size = BUFFER_SIZE;
216 let mapper =
217 DevMemMemoryMapper::create(address, size).expect("Could not mmap framebuffer.");
218
219 Self::new(mapper).unwrap()
220 }
221}
222
223impl<M: MemoryMapper> FpgaFramebuffer<M> {
224 fn new(memory: M) -> Result<Self, &'static str> {
225 Ok(Self { memory, ty_: None })
226 }
227
228 pub(crate) fn update_type_from_core(&mut self) {
229 let first = unsafe { self.header_offset(0) };
230 self.ty_ = if !first
231 .map(|h| h.attributes().triple_buffered())
232 .unwrap_or_default()
233 {
234 Some(FramebufferType::Single)
235 } else {
236 let (small, large) = unsafe {
237 (
238 self.header_offset(FramebufferType::TripleSmall.offset_of(1).unwrap()),
239 self.header_offset(FramebufferType::TripleLarge.offset_of(1).unwrap()),
240 )
241 };
242
243 match (small, large) {
244 (_, Some(_)) => Some(FramebufferType::TripleLarge),
245 (Some(_), None) => Some(FramebufferType::TripleSmall),
246 _ => None,
247 }
248 }
249 }
250
251 unsafe fn header_offset(&self, offset: usize) -> Option<FbHeader> {
253 FbHeader::from_memory(self.memory.as_ptr::<u8>().add(offset))
254 }
255
256 pub(crate) fn offset_of(&self, index: u8) -> Option<usize> {
257 self.ty_.and_then(|ty| ty.offset_of(index))
258 }
259
260 #[allow(unused)]
261 pub(crate) fn header(&self, index: u8) -> Option<FbHeader> {
262 self.offset_of(index)
263 .and_then(|offset| unsafe { self.header_offset(offset) })
264 }
265
266 fn first_header(&self) -> FbHeader {
267 unsafe { self.header_offset(0).unwrap() }
268 }
269
270 pub fn write(&mut self, data: &[u8]) -> Result<(), String> {
271 let header_len = self.first_header().header_len() as usize;
272 self.memory
273 .as_mut_range(header_len..(header_len + data.len()))
274 .copy_from_slice(data);
275
276 Ok(())
277 }
278
279 pub fn take_screenshot(&self) -> Result<DynamicImage, String> {
280 let header = self.first_header();
282
283 debug!("Header data: {:?}", header);
284
285 let height = header.height() as usize;
286 let width = header.width() as usize;
287 let line = header.line() as usize;
288 let start = self.memory.as_ptr::<u8>();
289 let fb = unsafe {
290 std::slice::from_raw_parts(start.add(header.header_len() as usize), line * height * 3)
291 };
292
293 let mut img = match header.scaler_pixel_format() {
295 ScalerPixelFormat::RGB16 => {
296 return Err("RGB565 not supported.".to_string());
297 }
298 ScalerPixelFormat::RGB24 => RgbImage::new(width as u32, height as u32),
299 ScalerPixelFormat::RGBA32 => {
300 return Err("RGBA32 not supported.".to_string());
301 }
302 ScalerPixelFormat::INVALID => {
303 return Err("Invalid Scaler PixelFormat.".to_string());
304 }
305 };
306
307 for y in 0..height {
308 let line = &fb[y * line..y * line + width * 3];
309 img.get_mut(y * width * 3..y * width * 3 + width * 3)
310 .unwrap()
311 .copy_from_slice(line);
312 }
313
314 Ok(DynamicImage::ImageRgb8(img))
315 }
316}