start to lay in code rework for booting on real chips

This commit is contained in:
bunnie 2024-04-26 18:16:12 +08:00
parent 05d2f3d6a9
commit a781b2e15d

View file

@ -2,8 +2,22 @@ use cramium_hal::iox::{Iox, IoxDir, IoxEnable, IoxFunction, IoxPort};
use cramium_hal::udma;
use utralib::generated::*;
// Notes about the reset vector location
// This can be set using fuses in the IFR (also called 'info') region
// The offset is an 8-bit value, which is shifted into a final location
// according to the following formula:
//
// let short_offset: u8 = OFFSET;
// let phys_offset: u32 = 0x6000_0000 + short_offset << 14;
//
// The RV32-IV IFR fuse location is at row 6, byte 8.
// Each row is 256 bits wide.
// This puts the byte-address hex offset at (6 * 256 + 8 * 8) / 8 = 0xC8
// within the IFR region. Total IFR region size is 0x200.
pub const RAM_SIZE: usize = utralib::generated::HW_SRAM_MEM_LEN;
pub const RAM_BASE: usize = utralib::generated::HW_SRAM_MEM;
pub const FLASH_BASE: usize = utralib::generated::HW_RERAM_MEM;
// Locate the hard-wired IFRAM allocations for UDMA
pub const UART_IFRAM_ADDR: usize = utralib::HW_IFRAM0_MEM + utralib::HW_IFRAM0_MEM_LEN - 4096;
@ -14,7 +28,7 @@ pub const SPIM_RAM_IFRAM_ADDR: usize = utralib::HW_IFRAM0_MEM + utralib::HW_IFRA
pub const SPIM_FLASH_IFRAM_ADDR: usize = utralib::HW_IFRAM0_MEM + utralib::HW_IFRAM0_MEM_LEN - 4 * 4096;
// location of kernel, as offset from the base of ReRAM. This needs to match up with what is in link.x.
pub const KERNEL_OFFSET: usize = 0x28000;
pub const KERNEL_OFFSET: usize = 0x4_0000;
#[cfg(feature = "cramium-soc")]
pub fn early_init() {
@ -26,44 +40,78 @@ pub fn early_init() {
//
// Not all design changes have a rhyme or reason at this stage -- sometimes "it just works,
// don't futz with it" is actually the answer that goes to production.
/*
// "actual SoC" parameters -- swap the comment here when silicon comes back
// not making it a "feature" because this is a one-way gate, I don't see
// any reason why we'd go back to using the emulator board if we have silicon.
use utralib::utra::sysctrl;
unsafe {
(0x400400a0 as *mut u32).write_volatile(0x1F598); // F
crate::println!("F: {:08x}", ((0x400400a0 as *const u32).read_volatile()));
let poke_array: [(u32, u32, bool); 12] = [
(0x400400a4, 0x2812, false), // MN
(0x400400a8, 0x3301, false), // Q
(0x40040090, 0x0032, true), // setpll
(0x40040014, 0x7f7f, false), // fclk
(0x40040018, 0x7f7f, false), // aclk
(0x4004001c, 0x3f3f, false), // hclk
(0x40040020, 0x1f1f, false), // iclk
(0x40040024, 0x0f0f, false), // pclk
(0x40040010, 0x0001, false), // sel0
(0x4004002c, 0x0032, true), // setcgu
(0x40040060, 0x0003, false), // aclk gates
(0x40040064, 0x0003, false), // hclk gates
];
for &(addr, dat, is_u32) in poke_array.iter() {
let rbk = if is_u32 {
(addr as *mut u32).write_volatile(dat);
(addr as *const u32).read_volatile()
} else {
(addr as *mut u16).write_volatile(dat as u16);
(addr as *const u16).read_volatile() as u32
};
if dat != rbk {
crate::println!("{:08x}(w) != {:08x}(r)", dat, rbk);
} else {
crate::println!("{:08x} ok", dat);
// this is MANDATORY for any chip stapbility in real silicon, as the initial
// clocks are too unstable to do anything otherwise. However, for the simulation
// environment, this can (should?) be dropped
let daric_cgu = sysctrl::HW_SYSCTRL_BASE as *mut u32;
daric_cgu.add(sysctrl::SFR_CGUSEL1.offset()).write_volatile(1); // 0: RC, 1: XTAL
daric_cgu.add(sysctrl::SFR_CGUFSCR.offset()).write_volatile(48); // external crystal is 48MHz
daric_cgu.add(sysctrl::SFR_CGUSET.offset()).write_volatile(0x32);
let duart = utra::duart::HW_DUART_BASE as *mut u32;
duart.add(utra::duart::SFR_CR.offset()).write_volatile(0);
duart.add(utra::duart::SFR_ETUC.offset()).write_volatile(24);
duart.add(utra::duart::SFR_CR.offset()).write_volatile(1);
}
// this block is mandatory in all cases to get clocks set into some consistent, expected mode
unsafe {
let daric_cgu = sysctrl::HW_SYSCTRL_BASE as *mut u32;
daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_0.offset()).write_volatile(0x7f7f);
daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_1.offset()).write_volatile(0x7f7f);
daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_2.offset()).write_volatile(0x3f3f);
daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_3.offset()).write_volatile(0x1f1f);
daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_4.offset()).write_volatile(0x0f0f);
daric_cgu.add(utra::sysctrl::SFR_ACLKGR.offset()).write_volatile(0xFF);
daric_cgu.add(utra::sysctrl::SFR_HCLKGR.offset()).write_volatile(0xFF);
daric_cgu.add(utra::sysctrl::SFR_ICLKGR.offset()).write_volatile(0xFF);
daric_cgu.add(utra::sysctrl::SFR_PCLKGR.offset()).write_volatile(0xFF);
daric_cgu.add(utra::sysctrl::SFR_CGUSET.offset()).write_volatile(0x32);
let duart = utra::duart::HW_DUART_BASE as *mut u32;
// enable DUART
duart.add(utra::duart::SFR_CR.offset()).write_volatile(1);
}
// unsafe, direct-writes to address offsets are used here instead of the UTRA abstraction
// because there are some quirks in the early boot path that make the system more stable
// if all register accesses are in-lined.
#[cfg(feature = "boot-delay")]
unsafe {
// this block should immediately follow the CGU setup
let duart = utra::duart::HW_DUART_BASE as *mut u32;
// ~2 second delay for debugger to attach
let msg = b"boot\r";
for j in 0..20_000 {
// variable count of .'s to create a sense of motion on the console
for _ in 0..j & 0x7 {
while duart.add(utra::duart::SFR_SR.offset()).read_volatile() != 0 {}
duart.add(utra::duart::SFR_TXD.offset()).write_volatile('.' as char as u32);
}
for &b in msg {
while duart.add(utra::duart::SFR_SR.offset()).read_volatile() != 0 {}
duart.add(utra::duart::SFR_TXD.offset()).write_volatile(b as char as u32);
}
}
} */
}
#[cfg(feature = "sram-margin")]
unsafe {
// set SRAM delay to max - opens up timing margin as much a possible, supposedly?
let sram_ctl = utra::coresub_sramtrm::HW_CORESUB_SRAMTRM_BASE as *mut u32;
let waitcycles = 3;
sram_ctl.add(utra::coresub_sramtrm::SFR_SRAM0.offset()).write_volatile(
(sram_ctl.add(utra::coresub_sramtrm::SFR_SRAM0.offset()).read_volatile() & !0x18)
| ((waitcycles << 3) & 0x18),
);
sram_ctl.add(utra::coresub_sramtrm::SFR_SRAM1.offset()).write_volatile(
(sram_ctl.add(utra::coresub_sramtrm::SFR_SRAM1.offset()).read_volatile() & !0x18)
| ((waitcycles << 3) & 0x18),
);
}
// SoC emulator board parameters (deals with MMCM instead of PLL)
// Remove this once we feel confident we're sticking with SoC hardware.
/*
unsafe {
let poke_array: [(u32, u32, bool); 9] = [
(0x40040030, 0x0001, true), // cgusel1
@ -90,7 +138,7 @@ pub fn early_init() {
crate::println!("{:08x} ok", dat);
}
}
}
} */
// Configure the UDMA UART. This UART's settings will be used as the initial console UART.
// This is configured in the loader so that the log crate does not have a dependency
@ -125,7 +173,7 @@ pub fn early_init() {
);
let baudrate: u32 = 115200;
let freq: u32 = 100_000_000;
let freq: u32 = 45_882_000;
// the address of the UART buffer is "hard-allocated" at an offset one page from the top of
// IFRAM0. This is a convention that must be respected by the UDMA UART library implementation
@ -154,7 +202,7 @@ pub fn early_init() {
// do a PL230/PIO test. Toggles PB15 (PIO0) with an LFSR sequence.
let mut pl230 = xous_pl230::Pl230::new();
xous_pl230::pl230_tests::units::basic_tests(&mut pl230);
xous_pl230::pl230_tests::units::pio_test(&mut pl230);
// xous_pl230::pl230_tests::units::pio_test(&mut pl230);
const BANNER: &'static str = "\n\rKeep pressing keys to continue boot...\r\n";
udma_uart.write(BANNER.as_bytes());
@ -232,3 +280,120 @@ fn test_duart() {
#[cfg(feature = "platform-tests")]
pub fn platform_tests() { test_duart(); }
pub unsafe fn init_clock_asic(freq_hz: u32) {
let daric_cgu = sysctrl::HW_SYSCTRL_BASE as *mut u32;
const F_MHZ: u32 = 1_000_000;
const FREQ_0: u32 = 16 * F_MHZ;
const TBL_Q: [u16; 7] = [
// keep later DIV even number as possible
0x7777, // 16-32 MHz
0x7737, // 32-64
0x3733, // 64-128
0x3313, // 128-256
0x3311, // 256-512 // keep ~ 100MHz
0x3301, // 512-1024
0x3301, /* 1024-1500
* 0x1303, // 256-512
* 0x0103, // 512-1024
* 0x0001, // 1024-2048 */
];
const TBL_MUL: [u32; 7] = [
64, // 16-32 MHz
32, // 32-64
16, // 64-128
8, // 128-256
4, // 256-512
2, // 512-1024
2, // 1024-2048
];
const M: u32 = 24 - 1;
report_api(0xc0c0_0000);
let f16_mhz_log2 = (freq_hz / FREQ_0).ilog2() as usize;
report_api(f16_mhz_log2 as u32);
let n_fxp24: u64 = (((freq_hz as u64) << 24) * TBL_MUL[f16_mhz_log2] as u64) / (2 * F_MHZ as u64);
report_api(n_fxp24 as u32);
report_api((n_fxp24 >> 32) as u32);
let n_frac: u32 = (n_fxp24 & 0x00ffffff) as u32;
report_api(n_frac);
let pllmn = ((M << 12) & 0x0001F000) | ((n_fxp24 >> 24) & 0x00000fff) as u32;
report_api(pllmn);
let pllf = n_frac | (if 0 == n_frac { 0 } else { 1 << 24 });
report_api(pllf);
let pllq = TBL_Q[f16_mhz_log2] as u32;
report_api(pllq);
daric_cgu.add(sysctrl::SFR_CGUSEL1.offset()).write_volatile(1); // 0: RC, 1: XTAL
daric_cgu.add(sysctrl::SFR_CGUFSCR.offset()).write_volatile(48); // external crystal is 48MHz
daric_cgu.add(sysctrl::SFR_CGUSET.offset()).write_volatile(0x32);
if freq_hz < 1_000_000 {
daric_cgu.add(sysctrl::SFR_IPCOSC.offset()).write_volatile(freq_hz);
daric_cgu.add(sysctrl::SFR_IPCARIPFLOW.offset()).write_volatile(0x32); // commit, must write 32
}
// switch to OSC
daric_cgu.add(sysctrl::SFR_CGUSEL0.offset()).write_volatile(0); // clktop sel, 0:clksys, 1:clkpll0
daric_cgu.add(sysctrl::SFR_CGUSET.offset()).write_volatile(0x32); // commit
if 0 == freq_hz {
// do nothing
} else {
// PD PLL
daric_cgu
.add(sysctrl::SFR_IPCLPEN.offset())
.write_volatile(daric_cgu.add(sysctrl::SFR_IPCLPEN.offset()).read_volatile() | 0x02);
daric_cgu.add(sysctrl::SFR_IPCARIPFLOW.offset()).write_volatile(0x32); // commit, must write 32
// delay
for _ in 0..1024 {
unsafe { core::arch::asm!("nop") };
}
for _ in 0..4 {
report_api(0xc0c0_dddd);
}
// printf ("%s(%4" PRIu32 "MHz) M = 24, N = %4lu.%08lu, Q = %2lu\n",
// __FUNCTION__, freqHz / 1000000, (uint32_t)(n_fxp24 >>
// 24).write_volatile((uint32_t)((uint64_t)(n_fxp24 & 0x00ffffff) * 100000000/(1UL
// <<24)).write_volatile(TBL_MUL[f16MHzLog2]);
daric_cgu.add(sysctrl::SFR_IPCPLLMN.offset()).write_volatile(pllmn); // 0x1F598; // ??
daric_cgu.add(sysctrl::SFR_IPCPLLF.offset()).write_volatile(pllf); // ??
daric_cgu.add(sysctrl::SFR_IPCPLLQ.offset()).write_volatile(pllq); // ?? TODO select DIV for VCO freq
// VCO bias CPP bias CPI bias
// 1 2 3
// DARIC_IPC->ipc = (3 << 6) | (5 << 3) | (5);
daric_cgu.add(sysctrl::SFR_IPCCR.offset()).write_volatile((1 << 6) | (2 << 3) | (3));
daric_cgu.add(sysctrl::SFR_IPCARIPFLOW.offset()).write_volatile(0x32); // commit, must write 32
daric_cgu
.add(sysctrl::SFR_IPCLPEN.offset())
.write_volatile(daric_cgu.add(sysctrl::SFR_IPCLPEN.offset()).read_volatile() & !0x02);
daric_cgu.add(sysctrl::SFR_IPCARIPFLOW.offset()).write_volatile(0x32); // commit, must write 32
// delay
for _ in 0..1024 {
unsafe { core::arch::asm!("nop") };
}
for _ in 0..4 {
report_api(0xc0c0_eeee);
}
// printf("read reg a0 : %08" PRIx32"\n", *((volatile uint32_t* )0x400400a0));
// printf("read reg a4 : %04" PRIx16"\n", *((volatile uint16_t* )0x400400a4));
// printf("read reg a8 : %04" PRIx16"\n", *((volatile uint16_t* )0x400400a8));
// TODO wait/poll lock status?
daric_cgu.add(sysctrl::SFR_CGUSEL0.offset()).write_volatile(1); // clktop sel, 0:clksys, 1:clkpll0
daric_cgu.add(sysctrl::SFR_CGUSET.offset()).write_volatile(0x32); // commit
report_api(0xc0c0_ffff);
// printf (" MN: 0x%05x, F: 0x%06x, Q: 0x%04x\n",
// DARIC_IPC->pll_mn, DARIC_IPC->pll_f, DARIC_IPC->pll_q);
// printf (" LPEN: 0x%01x, OSC: 0x%04x, BIAS: 0x%04x,\n",
// DARIC_IPC->lpen, DARIC_IPC->osc, DARIC_IPC->ipc);
}
report_api(0xc0c0_0007);
}