Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 61 additions & 5 deletions SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ def to_c_uint32(x):
return "{" + 'U,'.join(map(str, nums)) + "U}"


def build_project(project_name, project, main, extra_flags):
def build_project(project_name, project, main, extra_flags, extra_main_sources=None):
if extra_main_sources is None:
extra_main_sources = []
project_dir = Dir(f'./board/obj/{project_name}/')

flags = project["FLAGS"] + extra_flags + common_flags + [
Expand Down Expand Up @@ -101,22 +103,67 @@ def build_project(project_name, project, main, extra_flags):

startup = env.Object(project["STARTUP_FILE"])

# Shared source files (compiled separately for bootstub and main)
shared_sources = [
"./board/libc.c",
"./board/crc.c",
"./board/provision.c",
"./board/early_init.c",
"./board/drivers/registers.c",
"./board/drivers/interrupts.c",
"./board/drivers/spi.c",
"./board/drivers/usb.c",
"./board/stm32h7/peripherals.c",
"./board/stm32h7/clock.c",
"./board/stm32h7/llusb.c",
"./board/stm32h7/llspi.c",
"./board/stm32h7/lladc.c",
"./board/stm32h7/sound.c",
"./board/drivers/harness.c",
"./board/drivers/clock_source.c",
]

# Main-only source files used by ALL targets (not used in bootstub)
main_only_sources = [
"./board/drivers/uart.c",
"./board/drivers/can_common.c",
"./board/drivers/fdcan.c",
"./board/can_comms.c",
"./board/stm32h7/lluart.c",
"./board/stm32h7/llfdcan.c",
] + extra_main_sources

# Bootstub-only source files
bootstub_only_sources = [
"./board/flasher.c",
"./board/stm32h7/llflash.c",
]

# Build bootstub
bs_env = env.Clone()
bs_env.Append(CFLAGS="-DBOOTSTUB", ASFLAGS="-DBOOTSTUB", LINKFLAGS="-DBOOTSTUB")

# Build shared sources with bootstub flags (unique object names to avoid conflicts)
bs_shared_objs = [bs_env.Object(f"{project_dir}/bs_{os.path.basename(s)}.o", s) for s in shared_sources]
bs_only_objs = [bs_env.Object(s) for s in bootstub_only_sources]

bs_elf = bs_env.Program(f"{project_dir}/bootstub.elf", [
startup,
"./board/crypto/rsa.c",
"./board/crypto/sha.c",
"./board/bootstub.c",
])
] + bs_shared_objs + bs_only_objs)
bs_env.Objcopy(f"./board/obj/bootstub.{project_name}.bin", bs_elf)

# Build main-only and shared sources for main
main_shared_objs = [env.Object(s) for s in shared_sources]
main_only_objs = [env.Object(s) for s in main_only_sources]

# Build + sign main (aka app)
main_elf = env.Program(f"{project_dir}/main.elf", [
startup,
main
], LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags)
main,
] + main_shared_objs + main_only_objs, LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags)
main_bin = env.Objcopy(f"{project_dir}/main.bin", main_elf)
sign_py = File(f"./board/crypto/sign.py").srcnode().relpath
env.Command(f"./board/obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}")
Expand Down Expand Up @@ -159,8 +206,17 @@ hh, ch, jh = version_hash("board/health.h"), version_hash(os.path.join(opendbc.I
common_flags += [f"-DHEALTH_PACKET_VERSION=0x{hh:08X}U", f"-DCAN_PACKET_VERSION_HASH=0x{ch:08X}U",
f"-DJUNGLE_HEALTH_PACKET_VERSION=0x{jh:08X}U"]

# Panda-specific main sources (not needed by body or jungle)
panda_main_sources = [
"./board/drivers/simple_watchdog.c",
"./board/drivers/fan.c",
"./board/drivers/bootkick.c",
"./board/main_comms.c",
"./board/stm32h7/llfan.c",
]

# panda fw
build_project("panda_h7", base_project_h7, "./board/main.c", [])
build_project("panda_h7", base_project_h7, "./board/main.c", [], extra_main_sources=panda_main_sources)

# panda jungle fw
flags = [
Expand Down
1 change: 1 addition & 0 deletions board/boards/board_declarations.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ typedef void (*board_set_bootkick)(BootState state);
typedef bool (*board_read_som_gpio)(void);
typedef void (*board_set_amp_enabled)(bool enabled);

typedef struct harness_configuration harness_configuration;
struct board {
harness_configuration *harness_config;
GPIO_TypeDef * const led_GPIO[3];
Expand Down
9 changes: 9 additions & 0 deletions board/body/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
#include <stdbool.h>

#include "board/config.h"

// ********************* Globals **********************
uint8_t hw_type = 0;
board *current_board;
uint32_t uptime_cnt = 0;
uint32_t heartbeat_counter = 0;
bool heartbeat_lost = false;
bool heartbeat_disabled = false;
bool siren_enabled = false;
#include "board/drivers/led.h"
#include "board/drivers/pwm.h"
#include "board/drivers/usb.h"
Expand Down
9 changes: 9 additions & 0 deletions board/bootstub.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
// ********************* Includes *********************
#include "board/config.h"

// ********************* Globals (from bootstub_declarations.h) **********************
uint8_t hw_type = 0;
board *current_board;
#ifdef PANDA_JUNGLE
uint8_t harness_orientation = 0U;
uint8_t can_mode = 0U;
uint8_t ignition = 0U;
#endif

#include "board/drivers/led.h"
#include "board/drivers/pwm.h"
#include "board/drivers/usb.h"
Expand Down
5 changes: 2 additions & 3 deletions board/bootstub_declarations.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ void puth2(unsigned int i){ UNUSED(i); }
__attribute__((unused)) static void puth4(unsigned int i){ UNUSED(i); }
void hexdump(const void *a, int l){ UNUSED(a); UNUSED(l); }
typedef struct board board;
typedef struct harness_configuration harness_configuration;
void pwm_init(TIM_TypeDef *TIM, uint8_t channel);
void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage);
// No UART support in bootloader
typedef struct uart_ring uart_ring;
void uart_init(uart_ring *q, int baud) { UNUSED(q); UNUSED(baud); }

// ********************* Globals **********************
uint8_t hw_type = 0;
board *current_board;
extern uint8_t hw_type;
extern board *current_board;
158 changes: 158 additions & 0 deletions board/can_comms.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#include <stdbool.h>
#include <stdint.h>
#include "stm32h7xx.h"
#include "stm32h7xx_hal_gpio_ex.h"
#include "board/drivers/drivers.h"
#include "board/utils.h"
#include "board/libc.h"
#include "board/comms_definitions.h"

// Constants from config.h
#define USBPACKET_MAX_SIZE 0x40U
#define MAX_CAN_MSGS_PER_USB_BULK_TRANSFER 51U
#define MAX_CAN_MSGS_PER_SPI_BULK_TRANSFER 170U

/*
CAN transactions to and from the host come in the form of
a certain number of CANPacket_t. The transaction is split
into multiple transfers or chunks.

CAN packet byte layout (wire format used by comms_can_{read,write}):
+--------+--------+--------+--------+--------+--------+--------+------------------------------+
| byte 0 | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | ... byte 13 / byte 69 |
+--------+--------+--------+--------+--------+--------+--------+------------------------------+
| DLC | addr | addr | addr | flags | cksum | data0 | ... data7 / data63 |
| bus | | | | | | | (classic CAN / CAN FD) |
| fd | | | | | | | |
+--------+--------+--------+--------+--------+--------+--------+------------------------------+
Byte/bit fields:
byte 0: DLC[7:4], bus[3:1], fd[0]
bytes 1..4: (addr << 3) | (extended << 2) | (returned << 1) | rejected
byte 5: checksum = XOR(header[0..4] + payload)
bytes 6..13 (classic CAN, up to 8 bytes) / bytes 6..69 (CAN FD, up to 64 bytes): payload

USB/SPI transfer chunking used by this file:
+--------------------------------------------+ ... +--------------------------------------------+
| transport chunk 0 | | transport chunk N |
+--------------------------------------------+ +--------------------------------------------+
| concatenated CANPacket_t bytes | | continuation and/or next CANPacket_t bytes |
| (no per-64-byte counter/header in protocol)| | |
+--------------------------------------------+ +--------------------------------------------+

* comms_can_read outputs this buffer in chunks of a specified length.
chunks are always the given length, except the last one.
* comms_can_write reads in this buffer in chunks.
* both functions maintain an overflow buffer for a partial CANPacket_t that
spans multiple transfers/chunks.
* the overflow buffers are reset by a dedicated control transfer handler,
which is sent by the host on each start of a connection.
*/

typedef struct {
uint32_t ptr;
uint32_t tail_size;
uint8_t data[72];
} asm_buffer;

static asm_buffer can_read_buffer = {.ptr = 0U, .tail_size = 0U};

int comms_can_read(uint8_t *data, uint32_t max_len) {
uint32_t pos = 0U;

// Send tail of previous message if it is in buffer
if (can_read_buffer.ptr > 0U) {
uint32_t overflow_len = MIN(max_len - pos, can_read_buffer.ptr);
(void)memcpy(&data[pos], can_read_buffer.data, overflow_len);
pos += overflow_len;
(void)memcpy(can_read_buffer.data, &can_read_buffer.data[overflow_len], can_read_buffer.ptr - overflow_len);
can_read_buffer.ptr -= overflow_len;
}

if (can_read_buffer.ptr == 0U) {
// Fill rest of buffer with new data
CANPacket_t can_packet;
while ((pos < max_len) && can_pop(&can_rx_q, &can_packet)) {
uint32_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[can_packet.data_len_code];
if ((pos + pckt_len) <= max_len) {
(void)memcpy(&data[pos], (uint8_t*)&can_packet, pckt_len);
pos += pckt_len;
} else {
(void)memcpy(&data[pos], (uint8_t*)&can_packet, max_len - pos);
can_read_buffer.ptr += pckt_len - (max_len - pos);
// cppcheck-suppress objectIndex
(void)memcpy(can_read_buffer.data, &((uint8_t*)&can_packet)[(max_len - pos)], can_read_buffer.ptr);
pos = max_len;
}
}
}

return pos;
}

static asm_buffer can_write_buffer = {.ptr = 0U, .tail_size = 0U};

// send on CAN
void comms_can_write(const uint8_t *data, uint32_t len) {
uint32_t pos = 0U;

// Assembling can message with data from buffer
if (can_write_buffer.ptr != 0U) {
if (can_write_buffer.tail_size <= (len - pos)) {
// we have enough data to complete the buffer
CANPacket_t to_push = {0};
(void)memcpy(&can_write_buffer.data[can_write_buffer.ptr], &data[pos], can_write_buffer.tail_size);
can_write_buffer.ptr += can_write_buffer.tail_size;
pos += can_write_buffer.tail_size;

// send out
(void)memcpy((uint8_t*)&to_push, can_write_buffer.data, can_write_buffer.ptr);
can_send(&to_push, to_push.bus, false);

// reset overflow buffer
can_write_buffer.ptr = 0U;
can_write_buffer.tail_size = 0U;
} else {
// maybe next time
uint32_t data_size = len - pos;
(void) memcpy(&can_write_buffer.data[can_write_buffer.ptr], &data[pos], data_size);
can_write_buffer.tail_size -= data_size;
can_write_buffer.ptr += data_size;
pos += data_size;
}
}

// rest of the message
while (pos < len) {
uint32_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[(data[pos] >> 4U)];
if ((pos + pckt_len) <= len) {
CANPacket_t to_push = {0};
(void)memcpy((uint8_t*)&to_push, &data[pos], pckt_len);
can_send(&to_push, to_push.bus, false);
pos += pckt_len;
} else {
(void)memcpy(can_write_buffer.data, &data[pos], len - pos);
can_write_buffer.ptr = len - pos;
can_write_buffer.tail_size = pckt_len - can_write_buffer.ptr;
pos += can_write_buffer.ptr;
}
}

refresh_can_tx_slots_available();
}

void comms_can_reset(void) {
can_write_buffer.ptr = 0U;
can_write_buffer.tail_size = 0U;
can_read_buffer.ptr = 0U;
can_read_buffer.tail_size = 0U;
}

// TODO: make this more general!
void refresh_can_tx_slots_available(void) {
if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_USB_BULK_TRANSFER)) {
can_tx_comms_resume_usb();
}
if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_SPI_BULK_TRANSFER)) {
can_tx_comms_resume_spi();
}
}
Loading
Loading