add a graphics test routine for OQC

invoked via the GAM, callable exactly once per boot for up
to 60 seconds.
This commit is contained in:
bunnie 2021-09-16 01:00:22 +08:00
parent f0dd512d7d
commit 4882016d08
11 changed files with 162 additions and 10 deletions

1
Cargo.lock generated
View file

@ -843,6 +843,7 @@ dependencies = [
"num-traits",
"rkyv 0.4.3",
"susres",
"ticktimer-server",
"trng",
"utralib",
"xous",

View file

@ -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,
}

View file

@ -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};

View file

@ -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);}
}

View file

@ -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"}

View file

@ -95,6 +95,9 @@ pub(crate) enum Opcode {
BulkReadFonts,
RestartBulkRead,
/// generates a test pattern
TestPattern,
/// SuspendResume callback
SuspendResume,

View file

@ -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::<u32>()[..FB_SIZE]
}
/// Beneath this line are pure-HAL layer, and should not be user-visible
///

View file

@ -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

View file

@ -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};

View file

@ -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();

View file

@ -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(),