From 4882016d08a4befd0895195d32dd6725c131fbe4 Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 16 Sep 2021 01:00:22 +0800 Subject: [PATCH] add a graphics test routine for OQC invoked via the GAM, callable exactly once per boot for up to 60 seconds. --- Cargo.lock | 1 + services/gam/src/api.rs | 3 + services/gam/src/lib.rs | 7 ++ services/gam/src/main.rs | 13 +++ services/graphics-server/Cargo.toml | 1 + services/graphics-server/src/api.rs | 3 + .../graphics-server/src/backend/betrusted.rs | 12 +- .../graphics-server/src/backend/minifb.rs | 9 +- services/graphics-server/src/lib.rs | 8 ++ services/graphics-server/src/main.rs | 110 +++++++++++++++++- services/shellchat/src/cmds/test.rs | 5 +- 11 files changed, 162 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a65beac47..a25dfaf9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -843,6 +843,7 @@ dependencies = [ "num-traits", "rkyv 0.4.3", "susres", + "ticktimer-server", "trng", "utralib", "xous", diff --git a/services/gam/src/api.rs b/services/gam/src/api.rs index 3a70460c9..92899fa10 100644 --- a/services/gam/src/api.rs +++ b/services/gam/src/api.rs @@ -152,6 +152,9 @@ pub(crate) enum Opcode { /// Turn on Devboot Flag Devboot, + /// Show a test pattern. Can only call this once (to prevent abuse) + TestPattern, + Quit, } diff --git a/services/gam/src/lib.rs b/services/gam/src/lib.rs index 2eac5f859..71c7aedc7 100644 --- a/services/gam/src/lib.rs +++ b/services/gam/src/lib.rs @@ -320,6 +320,13 @@ impl Gam { ena, 0, 0, 0,) ).map(|_| ()) } + pub fn selftest(&self, duration_ms: usize) { + send_message( + self.conn, + Message::new_blocking_scalar(Opcode::TestPattern.to_usize().unwrap(), duration_ms, 0, 0, 0), + ) + .expect("couldn't self test"); + } } use core::sync::atomic::{AtomicU32, Ordering}; diff --git a/services/gam/src/main.rs b/services/gam/src/main.rs index 364e15859..fba352d03 100644 --- a/services/gam/src/main.rs +++ b/services/gam/src/main.rs @@ -642,6 +642,7 @@ fn xmain() -> ! { let mut powerdown_requested = false; let mut last_time: u64 = ticktimer.elapsed_ms(); + let mut did_test = false; // allow one go at the test pattern log::trace!("entering main loop"); #[cfg(not(any(target_os = "none", target_os = "xous")))] @@ -1017,6 +1018,18 @@ fn xmain() -> ! { if ena != 0 { gfx.set_devboot(true).expect("couldn't send devboot message"); } else { gfx.set_devboot(false).expect("couldn't send devboot message"); } }), + Some(Opcode::TestPattern) => msg_blocking_scalar_unpack!(msg, duration_ms, _, _, _, { + if !did_test { + did_test = true; + let checked_duration = if duration_ms > 60_000 { + 60_000 + } else { + duration_ms + }; + gfx.selftest(checked_duration); + } + xous::return_scalar(msg.sender, 1).expect("couldn't ack self test"); + }), Some(Opcode::Quit) => break, None => {log::error!("unhandled message {:?}", msg);} } diff --git a/services/graphics-server/Cargo.toml b/services/graphics-server/Cargo.toml index 87c93469a..ab0941ce5 100644 --- a/services/graphics-server/Cargo.toml +++ b/services/graphics-server/Cargo.toml @@ -13,6 +13,7 @@ log-server = {path = "../log-server"} xous = {path = "../../xous-rs"} susres = {path = "../susres"} trng = {path = "../trng"} +ticktimer-server = {path = "../ticktimer-server"} xous-ipc = {path = "../../xous-ipc"} xous-names = {path = "../xous-names"} diff --git a/services/graphics-server/src/api.rs b/services/graphics-server/src/api.rs index cdf514c94..8b3edfcd6 100644 --- a/services/graphics-server/src/api.rs +++ b/services/graphics-server/src/api.rs @@ -95,6 +95,9 @@ pub(crate) enum Opcode { BulkReadFonts, RestartBulkRead, + /// generates a test pattern + TestPattern, + /// SuspendResume callback SuspendResume, diff --git a/services/graphics-server/src/backend/betrusted.rs b/services/graphics-server/src/backend/betrusted.rs index 2b2f7e901..20d0e4987 100644 --- a/services/graphics-server/src/backend/betrusted.rs +++ b/services/graphics-server/src/backend/betrusted.rs @@ -3,10 +3,10 @@ use susres::{ManagedMem, RegManager, RegOrField, SuspendResume}; use utralib::generated::*; use xous::MemoryRange; -const FB_WIDTH_WORDS: usize = 11; +pub const FB_WIDTH_WORDS: usize = 11; const FB_WIDTH_PIXELS: usize = 336; -const FB_LINES: usize = 536; -const FB_SIZE: usize = FB_WIDTH_WORDS * FB_LINES; // 44 bytes by 536 lines +pub const FB_LINES: usize = 536; +pub const FB_SIZE: usize = FB_WIDTH_WORDS * FB_LINES; // 44 bytes by 536 lines const CONFIG_CLOCK_FREQUENCY: u32 = 100_000_000; pub struct XousDisplay { @@ -170,7 +170,7 @@ impl XousDisplay { unsafe { &mut *(self.fb.as_mut_ptr() as *mut [u32; FB_SIZE]) } } - pub fn blit_screen(&mut self, bmp: [u32; FB_SIZE]) { + pub fn blit_screen(&mut self, bmp: &[u32]) { let framebuffer = self.fb.as_mut_ptr() as *mut u32; for words in 0..FB_SIZE { @@ -183,6 +183,10 @@ impl XousDisplay { while self.busy() {} } + pub fn as_slice(&self) -> &[u32] { + &self.fb.as_slice::()[..FB_SIZE] + } + /// Beneath this line are pure-HAL layer, and should not be user-visible /// diff --git a/services/graphics-server/src/backend/minifb.rs b/services/graphics-server/src/backend/minifb.rs index dec9736bf..74d180026 100644 --- a/services/graphics-server/src/backend/minifb.rs +++ b/services/graphics-server/src/backend/minifb.rs @@ -8,7 +8,9 @@ const HEIGHT: usize = 536; /// Width of the screen in 32-bit words const WIDTH_WORDS: usize = 11; -const FB_SIZE: usize = WIDTH_WORDS * HEIGHT; // 44 bytes by 536 lines +pub const FB_WIDTH_WORDS: usize = WIDTH_WORDS; +pub const FB_LINES: usize = HEIGHT; +pub const FB_SIZE: usize = WIDTH_WORDS * HEIGHT; // 44 bytes by 536 lines const MAX_FPS: u64 = 60; const DARK_COLOUR: u32 = 0xB5B5AD; @@ -83,11 +85,14 @@ impl XousDisplay { Point::new(WIDTH as i16, HEIGHT as i16) } - pub fn blit_screen(&mut self, bmp: [u32; FB_SIZE]) { + pub fn blit_screen(&mut self, bmp: &[u32]) { for (dest, src) in self.emulated_buffer.iter_mut().zip(bmp.iter()) { *dest = *src; } } + pub fn as_slice(&self) -> &[u32] { + &self.emulated_buffer + } pub fn native_buffer(&mut self) -> &mut [u32; FB_SIZE] { &mut self.emulated_buffer diff --git a/services/graphics-server/src/lib.rs b/services/graphics-server/src/lib.rs index 1592747ec..36ed661e3 100644 --- a/services/graphics-server/src/lib.rs +++ b/services/graphics-server/src/lib.rs @@ -244,6 +244,14 @@ impl Gfx { ) .expect("couldn't reset bulk read"); } + + pub fn selftest(&self, duration_ms: usize) { + send_message( + self.conn, + Message::new_blocking_scalar(Opcode::TestPattern.to_usize().unwrap(), duration_ms, 0, 0, 0), + ) + .expect("couldn't self test"); + } } use core::sync::atomic::{AtomicU32, Ordering}; diff --git a/services/graphics-server/src/main.rs b/services/graphics-server/src/main.rs index 89c9d5a61..d75eb1b7f 100644 --- a/services/graphics-server/src/main.rs +++ b/services/graphics-server/src/main.rs @@ -27,7 +27,7 @@ mod fontmap; use api::BulkRead; fn draw_boot_logo(display: &mut XousDisplay) { - display.blit_screen(poweron::LOGO_MAP); + display.blit_screen(&poweron::LOGO_MAP); } #[cfg(any(target_os = "none", target_os = "xous"))] @@ -566,7 +566,7 @@ fn xmain() -> ! { .expect("could not return QueryGlyphProps request"); }), Some(Opcode::DrawSleepScreen) => msg_scalar_unpack!(msg, _, _, _, _, { - display.blit_screen(logo::LOGO_MAP); + display.blit_screen(&logo::LOGO_MAP); display.update(); display.redraw(); }), @@ -613,12 +613,118 @@ fn xmain() -> ! { bulkread.from_offset += readlen as u32; buf.replace(bulkread).unwrap(); } + Some(Opcode::TestPattern) => msg_blocking_scalar_unpack!(msg, duration, _, _, _, { + let mut stashmem = xous::syscall::map_memory( + None, + None, + ((backend::FB_SIZE * 4) + 4096) & !4095, + xous::MemoryFlags::R | xous::MemoryFlags::W, + ).expect("couldn't map stash frame buffer"); + let stash = &mut stashmem.as_slice_mut()[..backend::FB_SIZE]; + for (&src, dst) in display.as_slice().iter().zip(stash.iter_mut()) { + *dst = src; + } + for lines in 0..backend::FB_LINES { // mark all lines dirty + stash[lines * backend::FB_WIDTH_WORDS + (backend::FB_WIDTH_WORDS - 1)] |= 0x1_0000; + } + + let ticktimer = ticktimer_server::Ticktimer::new().unwrap(); + let start_time = ticktimer.elapsed_ms(); + let mut testmem = xous::syscall::map_memory( + None, + None, + ((backend::FB_SIZE * 4) + 4096) & !4095, + xous::MemoryFlags::R | xous::MemoryFlags::W, + ).expect("couldn't map stash frame buffer"); + let testpat = &mut testmem.as_slice_mut()[..backend::FB_SIZE]; + const DWELL: usize = 1000; + while ticktimer.elapsed_ms() - start_time < duration as u64 { + // all black + for w in testpat.iter_mut() { + *w = 0; + } + for lines in 0..backend::FB_LINES { // mark dirty bits + testpat[lines * backend::FB_WIDTH_WORDS + (backend::FB_WIDTH_WORDS - 1)] |= 0x1_0000; + } + display.blit_screen(testpat); + display.update(); + display.redraw(); + ticktimer.sleep_ms(DWELL).unwrap(); + + // all white + for w in testpat.iter_mut() { + *w = 0xFFFF_FFFF; + } + // dirty bits already set + display.blit_screen(testpat); + display.update(); + display.redraw(); + ticktimer.sleep_ms(DWELL).unwrap(); + + // vertical bars + for lines in 0..backend::FB_LINES { + for words in 0..backend::FB_WIDTH_WORDS { + testpat[lines * backend::FB_WIDTH_WORDS + words] = 0xaaaa_aaaa; + } + } + display.blit_screen(testpat); + display.update(); + display.redraw(); + ticktimer.sleep_ms(DWELL).unwrap(); + + for lines in 0..backend::FB_LINES { + for words in 0..backend::FB_WIDTH_WORDS { + testpat[lines * backend::FB_WIDTH_WORDS + words] = 0x5555_5555; + } + } + display.blit_screen(testpat); + display.update(); + display.redraw(); + ticktimer.sleep_ms(DWELL).unwrap(); + + // horiz bars + for lines in 0..backend::FB_LINES { + for words in 0..backend::FB_WIDTH_WORDS { + if lines % 2 == 0 { + testpat[lines * backend::FB_WIDTH_WORDS + words] = 0x0; + } else { + testpat[lines * backend::FB_WIDTH_WORDS + words] = 0xffff_ffff; + } + } + testpat[lines * backend::FB_WIDTH_WORDS + (backend::FB_WIDTH_WORDS - 1)] |= 0x1_0000; + } + display.blit_screen(testpat); + display.update(); + display.redraw(); + ticktimer.sleep_ms(DWELL).unwrap(); + + for lines in 0..backend::FB_LINES { + for words in 0..backend::FB_WIDTH_WORDS { + if lines % 2 == 1 { + testpat[lines * backend::FB_WIDTH_WORDS + words] = 0x0; + } else { + testpat[lines * backend::FB_WIDTH_WORDS + words] = 0xffff_ffff; + } + } + testpat[lines * backend::FB_WIDTH_WORDS + (backend::FB_WIDTH_WORDS - 1)] |= 0x1_0000; + } + display.blit_screen(testpat); + display.update(); + display.redraw(); + ticktimer.sleep_ms(DWELL).unwrap(); + + } + display.blit_screen(stash); + + xous::return_scalar(msg.sender, duration).expect("couldn't ack test pattern"); + }), Some(Opcode::Quit) => break, None => { log::error!("received opcode scalar that is not handled"); } } display.update(); + display.redraw(); } log::trace!("main loop exit, destroying servers"); xns.unregister_server(sid).unwrap(); diff --git a/services/shellchat/src/cmds/test.rs b/services/shellchat/src/cmds/test.rs index 272a95ca4..5e6431201 100644 --- a/services/shellchat/src/cmds/test.rs +++ b/services/shellchat/src/cmds/test.rs @@ -400,14 +400,14 @@ impl<'a> ShellCmdApi<'a> for Test { "oqc" => { env.llio.wfi_override(true).unwrap(); //xous::rsyscall(xous::SysCall::IncreaseHeap(65536, xous::MemoryFlags::R | xous::MemoryFlags::W)).expect("couldn't increase our heap"); - /* // temp clear so renode can run ret.clear(); + #[cfg(any(target_os = "none", target_os = "xous"))] if 0x362f093 != self.jtag.get_id().unwrap() { write!(ret, "FAIL: JTAG self access").unwrap(); return Ok(Some(ret)); } + #[cfg(any(target_os = "none", target_os = "xous"))] env.com.set_backlight(196, 196).unwrap(); - */ log::info!("building modal"); const OQC_SERVER: &str = "_OQC server_"; let kbdtest_sid = env.xns.register_name(OQC_SERVER, Some(1)).unwrap(); @@ -425,6 +425,7 @@ impl<'a> ShellCmdApi<'a> for Test { GlyphStyle::Small, 8 ); + test_modal.gam.selftest(18_000); log::info!("making helper"); test_modal.spawn_helper(kbdtest_sid, test_modal.sid, TestOp::ModalRedraw.to_u32().unwrap(),