3333#![ no_std]
3434#![ no_main]
3535
36+ // -----------------------------------------------------------------------------
37+ // Sub-modules
38+ // -----------------------------------------------------------------------------
39+
40+ pub mod vga;
41+
3642// -----------------------------------------------------------------------------
3743// Imports
3844// -----------------------------------------------------------------------------
@@ -43,11 +49,20 @@ use defmt_rtt as _;
4349use embedded_hal:: digital:: v2:: OutputPin ;
4450use embedded_time:: rate:: * ;
4551use git_version:: git_version;
46- use hal:: clocks:: Clock ;
4752use panic_probe as _;
48- use pico;
49- use pico:: hal;
50- use pico:: hal:: pac;
53+ use pico:: {
54+ self ,
55+ hal:: {
56+ self ,
57+ pac:: { self , interrupt} ,
58+ } ,
59+ } ;
60+
61+ // -----------------------------------------------------------------------------
62+ // Types
63+ // -----------------------------------------------------------------------------
64+
65+ // None
5166
5267// -----------------------------------------------------------------------------
5368// Static and Const Data
@@ -61,58 +76,111 @@ use pico::hal::pac;
6176/// See `memory.x` for a definition of the `.boot2` section.
6277#[ link_section = ".boot2" ]
6378#[ used]
64- pub static BOOT2 : [ u8 ; 256 ] = rp2040_boot2:: BOOT_LOADER ;
79+ pub static BOOT2 : [ u8 ; 256 ] = rp2040_boot2:: BOOT_LOADER_W25Q080 ;
6580
6681/// BIOS version
6782const GIT_VERSION : & str = git_version ! ( ) ;
6883
69- // -----------------------------------------------------------------------------
70- // Types
71- // -----------------------------------------------------------------------------
72-
73- // None
84+ /// Create a new Text Console
85+ static TEXT_CONSOLE : vga:: TextConsole = vga:: TextConsole :: new ( ) ;
7486
7587// -----------------------------------------------------------------------------
7688// Functions
7789// -----------------------------------------------------------------------------
7890
91+ /// Prints to the screen
92+ #[ macro_export]
93+ macro_rules! print {
94+ ( $( $arg: tt) * ) => {
95+ {
96+ use core:: fmt:: Write as _;
97+ write!( & TEXT_CONSOLE , $( $arg) * ) . unwrap( ) ;
98+ }
99+ } ;
100+ }
101+
102+ /// Prints to the screen and puts a new-line on the end
103+ #[ macro_export]
104+ macro_rules! println {
105+ ( ) => ( print!( "\n " ) ) ;
106+ ( $( $arg: tt) * ) => {
107+ {
108+ use core:: fmt:: Write as _;
109+ writeln!( & TEXT_CONSOLE , $( $arg) * ) . unwrap( ) ;
110+ }
111+ } ;
112+ }
113+
79114/// This is the entry-point to the BIOS. It is called by cortex-m-rt once the
80115/// `.bss` and `.data` sections have been initialised.
81116#[ entry]
82117fn main ( ) -> ! {
118+ cortex_m:: interrupt:: disable ( ) ;
119+
83120 info ! ( "Neotron BIOS {} starting..." , GIT_VERSION ) ;
84121
85122 // Grab the singleton containing all the RP2040 peripherals
86123 let mut pac = pac:: Peripherals :: take ( ) . unwrap ( ) ;
87124 // Grab the singleton containing all the generic Cortex-M peripherals
88- let core = pac:: CorePeripherals :: take ( ) . unwrap ( ) ;
125+ let _core = pac:: CorePeripherals :: take ( ) . unwrap ( ) ;
126+
127+ // Reset the DMA engine. If we don't do this, starting from probe-run
128+ // (as opposed to a cold-start) is unreliable.
129+ pac. RESETS . reset . modify ( |_r, w| w. dma ( ) . set_bit ( ) ) ;
130+ cortex_m:: asm:: nop ( ) ;
131+ pac. RESETS . reset . modify ( |_r, w| w. dma ( ) . clear_bit ( ) ) ;
132+ while pac. RESETS . reset_done . read ( ) . dma ( ) . bit_is_clear ( ) { }
89133
90134 // Needed by the clock setup
91135 let mut watchdog = hal:: watchdog:: Watchdog :: new ( pac. WATCHDOG ) ;
92136
93- // Get ourselves up to a decent clock speed.
94- let clocks = hal:: clocks:: init_clocks_and_plls (
95- pico:: XOSC_CRYSTAL_FREQ ,
96- pac. XOSC ,
97- pac. CLOCKS ,
137+ // Run at 126 MHz SYS_PLL, 48 MHz, USB_PLL
138+
139+ let xosc = hal:: xosc:: setup_xosc_blocking ( pac. XOSC , pico:: XOSC_CRYSTAL_FREQ . Hz ( ) )
140+ . map_err ( |_x| false )
141+ . unwrap ( ) ;
142+
143+ // Configure watchdog tick generation to tick over every microsecond
144+ watchdog. enable_tick_generation ( ( pico:: XOSC_CRYSTAL_FREQ / 1_000_000 ) as u8 ) ;
145+
146+ let mut clocks = hal:: clocks:: ClocksManager :: new ( pac. CLOCKS ) ;
147+
148+ let pll_sys = hal:: pll:: setup_pll_blocking (
98149 pac. PLL_SYS ,
150+ xosc. operating_frequency ( ) . into ( ) ,
151+ hal:: pll:: PLLConfig {
152+ vco_freq : Megahertz ( 1512 ) ,
153+ refdiv : 1 ,
154+ post_div1 : 6 ,
155+ post_div2 : 2 ,
156+ } ,
157+ & mut clocks,
158+ & mut pac. RESETS ,
159+ )
160+ . map_err ( |_x| false )
161+ . unwrap ( ) ;
162+
163+ let pll_usb = hal:: pll:: setup_pll_blocking (
99164 pac. PLL_USB ,
165+ xosc. operating_frequency ( ) . into ( ) ,
166+ hal:: pll:: common_configs:: PLL_USB_48MHZ ,
167+ & mut clocks,
100168 & mut pac. RESETS ,
101- & mut watchdog,
102169 )
103- . ok ( )
170+ . map_err ( |_x| false )
104171 . unwrap ( ) ;
105172
106- info ! ( "Clocks OK" ) ;
173+ clocks
174+ . init_default ( & xosc, & pll_sys, & pll_usb)
175+ . map_err ( |_x| false )
176+ . unwrap ( ) ;
107177
108- // Create an object we can use to busy-wait for specified numbers of
109- // milliseconds. For this to work, it needs to know our clock speed.
110- let mut delay = cortex_m:: delay:: Delay :: new ( core. SYST , clocks. system_clock . freq ( ) . integer ( ) ) ;
178+ info ! ( "Clocks OK" ) ;
111179
112180 // sio is the *Single-cycle Input/Output* peripheral. It has all our GPIO
113181 // pins, as well as some mailboxes and other useful things for inter-core
114182 // communications.
115- let sio = hal:: sio:: Sio :: new ( pac. SIO ) ;
183+ let mut sio = hal:: sio:: Sio :: new ( pac. SIO ) ;
116184
117185 // Configure and grab all the RP2040 pins the Pico exposes.
118186 let pins = pico:: Pins :: new (
@@ -122,18 +190,54 @@ fn main() -> ! {
122190 & mut pac. RESETS ,
123191 ) ;
124192
193+ // Disable power save mode to force SMPS into low-efficiency, low-noise mode.
194+ let mut b_power_save = pins. b_power_save . into_push_pull_output ( ) ;
195+ b_power_save. set_high ( ) . unwrap ( ) ;
196+
197+ // Give H-Sync, V-Sync and 12 RGB colour pins to PIO0 to output video
198+ let _h_sync = pins. gpio0 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
199+ let _v_sync = pins. gpio1 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
200+ let _red0 = pins. gpio2 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
201+ let _red1 = pins. gpio3 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
202+ let _red2 = pins. gpio4 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
203+ let _red3 = pins. gpio5 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
204+ let _green0 = pins. gpio6 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
205+ let _green1 = pins. gpio7 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
206+ let _green2 = pins. gpio8 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
207+ let _green3 = pins. gpio9 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
208+ let _blue0 = pins. gpio10 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
209+ let _blue1 = pins. gpio11 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
210+ let _blue2 = pins. gpio12 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
211+ let _blue3 = pins. gpio13 . into_mode :: < hal:: gpio:: FunctionPio0 > ( ) ;
212+
125213 info ! ( "Pins OK" ) ;
126214
127- // Grab the LED pin
128- let mut led_pin = pins. led . into_push_pull_output ( ) ;
215+ vga:: init (
216+ pac. PIO0 ,
217+ pac. DMA ,
218+ & mut pac. RESETS ,
219+ & mut pac. PPB ,
220+ & mut sio. fifo ,
221+ & mut pac. PSM ,
222+ ) ;
223+
224+ TEXT_CONSOLE . set_text_buffer ( unsafe { & mut vga:: CHAR_ARRAY } ) ;
129225
130- // Do some blinky so we can see it work.
226+ info ! ( "VGA intialised" ) ;
227+
228+ let mut x: u32 = 0 ;
131229 loop {
132- debug ! ( "Loop..." ) ;
133- led_pin. set_high ( ) . unwrap ( ) ;
134- delay. delay_ms ( 500 ) ;
135- led_pin. set_low ( ) . unwrap ( ) ;
136- delay. delay_ms ( 500 ) ;
230+ println ! ( "x = {}" , x) ;
231+ x = x. wrapping_add ( 1 ) ;
232+ }
233+ }
234+
235+ /// Called when DMA raises IRQ0; i.e. when a DMA transfer to the pixel FIFO or
236+ /// the timing FIFO has completed.
237+ #[ interrupt]
238+ fn DMA_IRQ_0 ( ) {
239+ unsafe {
240+ vga:: irq ( ) ;
137241 }
138242}
139243
0 commit comments