Skip to content

Commit 9b830c9

Browse files
Clean up BMC reading.
Now there's a function for "read a register from the BMC". It also applies a small delay between the writing request and reading the response, which allows us to increase the clock speed to 4 MHz.
1 parent 5f661a3 commit 9b830c9

File tree

1 file changed

+103
-101
lines changed

1 file changed

+103
-101
lines changed

src/main.rs

Lines changed: 103 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,15 @@ pub mod vga;
5252
// -----------------------------------------------------------------------------
5353

5454
// Standard Library Stuff
55-
use core::{
56-
fmt::Write,
57-
sync::atomic::{AtomicBool, AtomicU8, Ordering},
58-
};
55+
use core::{fmt::Write, sync::atomic::AtomicBool};
5956

6057
// Third Party Stuff
6158
use cortex_m_rt::entry;
6259
use critical_section::Mutex;
6360
use defmt::info;
6461
use defmt_rtt as _;
6562
use embedded_hal::{
66-
blocking::spi::Transfer as _SpiTransfer, blocking::spi::Write as _SpiWrite,
63+
blocking::spi::{Transfer as _SpiTransfer, Write as _SpiWrite},
6764
digital::v2::OutputPin,
6865
};
6966
use fugit::RateExtU32;
@@ -104,6 +101,8 @@ struct Hardware {
104101
keyboard: pc_keyboard::Keyboard<pc_keyboard::layouts::Uk105Key, pc_keyboard::ScancodeSet2>,
105102
/// Our queue of HID events
106103
event_queue: heapless::Deque<neotron_common_bios::hid::HidEvent, 16>,
104+
/// A place to send/receive bytes to/from the BMC
105+
bmc_buffer: [u8; 64],
107106
}
108107

109108
/// Flips between true and false so we always send a unique read request
@@ -393,6 +392,9 @@ impl Hardware {
393392
/// Give the device 10us when we do a retry.
394393
const SPI_RETRY_CPU_CLOCKS: u32 = 10_000 / Self::NS_PER_CLOCK_CYCLE;
395394

395+
/// Give the BMC 4us to calculate its response
396+
const BMC_REQUEST_RESPONSE_DELAY_CLOCKS: u32 = 4_000 / Self::NS_PER_CLOCK_CYCLE;
397+
396398
/// Data Direction Register A on the MCP23S17
397399
const MCP23S17_DDRA: u8 = 0x00;
398400

@@ -553,18 +555,19 @@ impl Hardware {
553555
i2s_lr_clock: hal_pins.gpio28.into_mode(),
554556
pico_led: hal_pins.led.into_mode(),
555557
},
556-
// Set SPI up for 2 MHz clock, 8 data bits.
558+
// Set SPI up for 4 MHz clock, 8 data bits.
557559
spi_bus: hal::Spi::new(spi).init(
558560
resets,
559561
clocks.peripheral_clock.freq(),
560-
2_000_000.Hz(),
562+
4_000_000.Hz(),
561563
&embedded_hal::spi::MODE_0,
562564
),
563565
delay,
564566
debug_leds: 0,
565567
last_cs: 0,
566568
keyboard: pc_keyboard::Keyboard::new(pc_keyboard::HandleControl::Ignore),
567569
event_queue: heapless::Deque::new(),
570+
bmc_buffer: [0u8; 64],
568571
}
569572
}
570573

@@ -647,7 +650,7 @@ impl Hardware {
647650
/// SPI bus object), then de-activates the CS pin.
648651
fn with_bus_cs<F>(&mut self, cs: u8, func: F)
649652
where
650-
F: FnOnce(&mut hal::Spi<hal::spi::Enabled, pac::SPI0, 8_u8>),
653+
F: FnOnce(&mut hal::Spi<hal::spi::Enabled, pac::SPI0, 8_u8>, &mut [u8]),
651654
{
652655
// Only CS0..CS7 is valid
653656
let cs = cs & 0b111;
@@ -665,7 +668,7 @@ impl Hardware {
665668
cortex_m::asm::delay(Self::CS_BUS_SETUP_CPU_CLOCKS);
666669

667670
// Call function
668-
func(&mut self.spi_bus);
671+
func(&mut self.spi_bus, &mut self.bmc_buffer);
669672

670673
// Hold the CS pin a bit longer
671674
cortex_m::asm::delay(Self::CS_BUS_HOLD_CPU_CLOCKS);
@@ -685,6 +688,11 @@ impl Hardware {
685688
self.pins.noutput_en.set_high().unwrap();
686689
}
687690

691+
/// Configure the MCP23S17 correctly.
692+
///
693+
/// We have GPIOA as outputs (for the debug LEDs, HDD LED and 3-bit
694+
/// chip-select number). We have GPIOB as pulled-up inputs (for the eight
695+
/// interrupts).
688696
fn init_io_chip(&mut self) {
689697
// Undrive CS lines from decoder/buffer
690698
self.release_cs_lines();
@@ -705,122 +713,116 @@ impl Hardware {
705713
self.io_chip_write(Self::MCP23S17_GPPUB, 0xFF);
706714
}
707715

708-
/// Read the BMC firmware version string.
716+
/// Read from a BMC register.
709717
///
710-
/// You get 32 bytes of probably UTF-8 data.
711-
fn bmc_read_firmware_version(&mut self) -> Result<[u8; 32], ()> {
712-
let req = neotron_bmc_protocol::Request::new_read(false, 0x01, 32);
713-
let mut buffer = [0xFF; 64];
714-
buffer[0..=3].copy_from_slice(&req.as_bytes());
715-
self.with_bus_cs(0, |spi| {
716-
spi.transfer(&mut buffer).unwrap();
717-
});
718-
defmt::info!("buffer: {=[u8]:x}", buffer);
719-
let mut result = &buffer[..];
720-
let mut latency = 0;
721-
while !result.is_empty() && ((result[0] == 0xFF) || (result[0] == 0x00)) {
722-
latency += 1;
723-
result = &result[1..];
718+
/// It will perform a couple of retries, and then panic if the BMC did not respond correctly. It
719+
/// sets the Chip Select line and controls the IO chip automatically.
720+
///
721+
/// The number of bytes you want is set by the length of the `buffer` argument.
722+
///
723+
fn bmc_read_register(&mut self, register: u8, buffer: &mut [u8]) -> Result<(), ()> {
724+
if buffer.len() > self.bmc_buffer.len() {
725+
defmt::error!("Asked for too much data ({})", buffer.len());
726+
return Err(());
724727
}
725-
defmt::info!("latency: {}", latency);
726-
// 32 bytes of data requested, plus one bytes of response code and one byte of CRC
727-
if result.len() >= 34 {
728-
match neotron_bmc_protocol::Response::from_bytes(&result[0..34]) {
729-
Ok(res) => {
730-
if res.result == neotron_bmc_protocol::ResponseResult::Ok
731-
&& res.data.len() == 32
732-
{
733-
defmt::info!("Got BMC version {=[u8]:a}", res.data);
734-
let mut string_bytes = [0u8; 32];
735-
string_bytes.copy_from_slice(res.data);
736-
return Ok(string_bytes);
737-
} else {
738-
defmt::warn!(
739-
"Error getting BMC version: Error from BMC {:?} {=[u8]:x}",
740-
res.result,
741-
res.data
742-
);
743-
}
744-
}
745-
Err(e) => {
746-
defmt::warn!(
747-
"Error getting BMC version: Decoding Error {:?} {=[u8]:x}",
748-
e,
749-
result
750-
);
751-
}
752-
}
728+
if buffer.len() > usize::from(u8::MAX) {
729+
defmt::error!("Asked for too much data ({})", buffer.len());
730+
return Err(());
753731
}
754-
755-
Err(())
756-
}
757-
758-
/// Read the BMC PS/2 keyboard FIFO.
759-
///
760-
/// We ask for 8 bytes of data. We get `1` byte of 'length', then `N` bytes of valid data, and `32 - (N + 1)` bytes of padding.
761-
fn bmc_read_ps2_keyboard_fifo(&mut self, out_buffer: &mut [u8; 8]) -> Result<usize, ()> {
762-
static COUNTER: AtomicU8 = AtomicU8::new(0);
763-
let req = neotron_bmc_protocol::Request::new_read(USE_ALT.get(), 0x40, 8);
764-
for _retry in 0..4 {
765-
let mut buffer = [0xFF; 32];
766-
buffer[0..=3].copy_from_slice(&req.as_bytes());
767-
buffer[4] = COUNTER.load(Ordering::Relaxed);
768-
COUNTER.store(buffer[4].wrapping_add(1), Ordering::Relaxed);
769-
defmt::trace!("out: {=[u8]:02x}", buffer);
770-
self.with_bus_cs(0, |spi| {
771-
spi.transfer(&mut buffer).unwrap();
732+
let req =
733+
neotron_bmc_protocol::Request::new_read(USE_ALT.get(), register, buffer.len() as u8);
734+
let req_bytes = req.as_bytes();
735+
for _retries in 0..4 {
736+
// Clear the input buffer
737+
for byte in self.bmc_buffer.iter_mut() {
738+
*byte = 0xFF;
739+
}
740+
defmt::debug!("req: {=[u8; 4]:02x}", req_bytes);
741+
self.with_bus_cs(0, |spi, buffer| {
742+
// Send the request
743+
spi.write(&req_bytes).unwrap();
744+
cortex_m::asm::delay(Self::BMC_REQUEST_RESPONSE_DELAY_CLOCKS);
745+
// Get the response
746+
spi.transfer(buffer).unwrap();
772747
});
773-
defmt::trace!("in : {=[u8]:02x}", buffer);
774-
// Skip the first four bytes at least (that's our command, and also
775-
// the BMC FIFO length which might have crud in it). Then trip any padding.
776-
let mut result = &buffer[4..];
748+
// Trim the padding off the front
749+
let mut response_buffer = &self.bmc_buffer[..];
777750
let mut latency = 0;
778-
while !result.is_empty() && (result[0] == 0xFF || result[0] == 0x00) {
751+
while response_buffer.len() > 0
752+
&& ((response_buffer[0] == 0x00) || (response_buffer[0] == 0xFF))
753+
{
754+
response_buffer = &response_buffer[1..];
779755
latency += 1;
780-
result = &result[1..];
781756
}
782-
defmt::trace!("latency: {}", latency);
757+
defmt::debug!("res: {=[u8]:02x} ({})", response_buffer, latency);
758+
let expected_response_len = buffer.len() + 2;
783759
// 8 bytes of data requested, plus one bytes of response code and one byte of CRC
784-
if result.len() >= 10 {
785-
match neotron_bmc_protocol::Response::from_bytes(&result[0..10]) {
760+
if response_buffer.len() >= expected_response_len {
761+
match neotron_bmc_protocol::Response::from_bytes(
762+
&response_buffer[0..expected_response_len],
763+
) {
786764
Ok(res) => {
787765
if res.result == neotron_bmc_protocol::ResponseResult::Ok
788-
&& res.data.len() == 8
766+
&& res.data.len() == buffer.len()
789767
{
790-
if res.data[0] == 0 {
791-
defmt::trace!("Got no PS/2 bytes");
792-
} else {
793-
defmt::debug!("Got PS/2 bytes {=[u8]:x}", res.data);
794-
}
795-
for (dest, src) in out_buffer.iter_mut().zip(res.data.iter().skip(1)) {
796-
*dest = *src;
797-
}
798-
return Ok(res.data[0] as usize);
768+
buffer.copy_from_slice(res.data);
769+
return Ok(());
799770
} else {
800771
defmt::warn!(
801-
"Error getting keyboard bytes: Error from BMC {:?} {=[u8]:x}",
772+
"Error reading {} bytes from {}: Error from BMC {:?} {=[u8]:x}",
773+
buffer.len(),
774+
register,
802775
res.result,
803776
res.data
804777
);
778+
// No point retrying - we heardly them perfectly
779+
return Err(());
805780
}
806781
}
807782
Err(e) => {
808783
defmt::warn!(
809-
"Error getting BMC keyboard bytes: Decoding Error {:?} {=[u8]:x}",
784+
"Error reading {} bytes from {}: Decoding Error {:?} {=[u8]:x}",
785+
buffer.len(),
786+
register,
810787
e,
811-
result
788+
response_buffer
812789
);
813790
}
814791
}
815792
} else {
816-
defmt::warn!("Short packet!?");
793+
defmt::warn!("Short packet {=[u8]:x}", response_buffer);
817794
}
818-
819795
// Wait a bit before we try again
820796
cortex_m::asm::delay(Self::SPI_RETRY_CPU_CLOCKS);
821797
}
822-
// Ran out of retries
823-
panic!("KB retry timeout");
798+
panic!("Failed to talk to BMC after several retries.");
799+
}
800+
801+
/// Read the BMC firmware version string.
802+
///
803+
/// You get 32 bytes of probably UTF-8 data.
804+
fn bmc_read_firmware_version(&mut self) -> Result<[u8; 32], ()> {
805+
let mut firmware_version = [0u8; 32];
806+
self.bmc_read_register(0x01, &mut firmware_version)?;
807+
Ok(firmware_version)
808+
}
809+
810+
/// Read the BMC PS/2 keyboard FIFO.
811+
///
812+
/// We ask for 8 bytes of data. We get `1` byte of 'length', then `N` bytes of valid data, and `32 - (N + 1)` bytes of padding.
813+
fn bmc_read_ps2_keyboard_fifo(&mut self, out_buffer: &mut [u8; 8]) -> Result<usize, ()> {
814+
let mut fifo_data = [0u8; 9];
815+
self.bmc_read_register(0x40, &mut fifo_data)?;
816+
let bytes_in_fifo = fifo_data[0];
817+
if bytes_in_fifo == 0 {
818+
defmt::trace!("Got no PS/2 bytes");
819+
} else {
820+
defmt::debug!("Got PS/2 bytes {=[u8]:x}", &fifo_data[..]);
821+
}
822+
for (dest, src) in out_buffer.iter_mut().zip(fifo_data.iter().skip(1)) {
823+
*dest = *src;
824+
}
825+
return Ok(bytes_in_fifo as usize);
824826
}
825827
}
826828

@@ -1147,7 +1149,7 @@ pub extern "C" fn hid_get_event() -> common::Result<common::Option<common::hid::
11471149
Ok(None) => {
11481150
// Need more data
11491151
}
1150-
Err(e) => {
1152+
Err(_e) => {
11511153
panic!("Keyboard decode error!");
11521154
}
11531155
}
@@ -1378,14 +1380,14 @@ extern "C" fn video_get_palette(index: u8) -> common::Option<common::video::RGBC
13781380
}
13791381

13801382
/// Update the RGB palette
1381-
extern "C" fn video_set_palette(index: u8, rgb: common::video::RGBColour) {
1383+
extern "C" fn video_set_palette(_index: u8, _rgb: common::video::RGBColour) {
13821384
// TODO set the palette when we actually have one
13831385
}
13841386

13851387
/// Update all the RGB palette
13861388
unsafe extern "C" fn video_set_whole_palette(
1387-
palette: *const common::video::RGBColour,
1388-
length: usize,
1389+
_palette: *const common::video::RGBColour,
1390+
_length: usize,
13891391
) {
13901392
// TODO set the palette when we actually have one
13911393
}
@@ -1561,7 +1563,7 @@ pub extern "C" fn block_verify(
15611563
common::Result::Err(common::Error::Unimplemented)
15621564
}
15631565

1564-
extern "C" fn block_dev_eject(dev_id: u8) -> common::Result<()> {
1566+
extern "C" fn block_dev_eject(_dev_id: u8) -> common::Result<()> {
15651567
common::Result::Ok(())
15661568
}
15671569

0 commit comments

Comments
 (0)