serial_transfer: rewrite

This commit is contained in:
Zack Buhman 2024-10-26 04:53:07 -05:00
parent 71fb02e03d
commit ff7a03a75c
23 changed files with 815 additions and 253 deletions

View File

@ -36,9 +36,10 @@ def symmetric(ser, b):
l = []
mem = memoryview(b)
i = 0
chunk_size = 128
while i < len(b):
chunk_size = 128
if i % 1024 == 0:
print(i, end=' ')
sys.stdout.flush()
@ -49,6 +50,7 @@ def symmetric(ser, b):
if len(l) + chunk_size >= i:
break
time.sleep(1000 / 1000000)
chunk_size = min(chunk_size, len(b) - i)
assert chunk_size > 0, chunk_size
ser.write(mem[i:i+chunk_size])
@ -73,12 +75,12 @@ def start_data(ser, b):
size = len(b)
args = struct.pack("<II", size, dest)
ret = sync(ser, b'DATA' + args)
if ret != b'data\n' and ret != b'\ndata\n' and ret != b'\x00data\n':
if ret != b'data' and ret != b'data\n' and ret != b'\ndata\n' and ret != b'\x00data\n':
print(".", end=' ')
print(ret)
time.sleep(1)
time.sleep(10)
sys.stdout.flush()
sync(ser, b'prime', wait=0)
sync(ser, b'prime')
start_data(ser, b)
return
print("\nDATA")
@ -104,7 +106,6 @@ def do(ser, b):
args = struct.pack("<I", dest)
ret = sync(ser, b'JUMP' + args, wait=0)
print("JUMP", ret)
print("console:")
console(ser)
seen_length = 16
@ -121,6 +122,7 @@ framebuffer_mode = False
framebuffer = []
def console(ser):
print("console:")
global framebuffer_mode
global framebuffer

View File

@ -2,7 +2,7 @@ MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
DIR := $(dir $(MAKEFILE_PATH))
LIB ?= .
OPT ?= -Og
OPT ?= -O2
GENERATED ?=
AARCH = --isa=sh4 --little
@ -17,6 +17,11 @@ OBJARCH = -O elf32-shl -B sh4
TARGET = sh4-none-elf-
START_OBJ = \
start.o \
runtime.o \
sh7091/cache.o
IP_OBJ = \
systemid.o \
toc.o \
@ -29,8 +34,15 @@ IP_OBJ = \
sg_are02.o \
sg_are03.o \
sg_are04.o \
sg_ini.o \
aip.o
$(START_OBJ) \
example/serial_transfer.o \
sh7091/serial.o \
serial_load.o \
maple/maple.o \
font/portfolio_6x8/portfolio_6x8.data.o \
crc32.o
#sg_ini.o \
#aip.o
%.o: %.obj
$(OBJCOPY) -g \
@ -40,11 +52,6 @@ IP_OBJ = \
ip.elf: $(IP_OBJ)
$(LD) --orphan-handling=error --print-memory-usage -T $(LIB)/ip.lds $^ -o $@
START_OBJ = \
start.o \
runtime.o \
sh7091/cache.o
include base.mk
sine.pcm: common.mk

90
crc32.c Normal file
View File

@ -0,0 +1,90 @@
#include <stdint.h>
#include "crc32.h"
// Automatically generated CRC function
// polynomial: 0x104C11DB7
const uint32_t crc32_table[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4,
};
uint32_t
crc32_update(uint32_t crc, const uint8_t * data, uint32_t len)
{
while (len > 0) {
crc = crc32_table[*data ^ ((crc >> 24) & 0xff)] ^ (crc << 8);
data++;
len--;
}
return crc;
}
uint32_t
crc32(const uint8_t * buf, uint32_t len)
{
return crc32_update(0xffffffff, buf, len) ^ 0xffffffff;
}

19
crc32.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const uint32_t crc32_table[256];
uint32_t
crc32_update(uint32_t crc, const uint8_t *data, uint32_t len);
uint32_t
crc32(const uint8_t *buf, uint32_t len);
#ifdef __cplusplus
}
#endif

View File

@ -360,7 +360,8 @@ SERIAL_TRANSFER_OBJ = \
sh7091/serial.o \
serial_load.o \
maple/maple.o \
font/portfolio_6x8/portfolio_6x8.data.o
font/portfolio_6x8/portfolio_6x8.data.o \
crc32.o
example/serial_transfer.elf: LDSCRIPT = $(LIB)/loader.lds
example/serial_transfer.elf: $(START_OBJ) $(SERIAL_TRANSFER_OBJ)

View File

@ -160,6 +160,7 @@ void main()
init_texture_memory(opb_size);
uint32_t frame_ix = 0;
uint32_t frame = 0;
while (true) {
ta_polygon_converter_init(opb_size.total(),
@ -179,5 +180,9 @@ void main()
theta += half_degree;
frame_ix = (frame_ix + 1) & 1;
frame += 1;
if (frame > 10)
break;
}
}

View File

@ -14,6 +14,8 @@
#include "serial_load.hpp"
#include "crc32.h"
extern uint32_t _binary_font_portfolio_6x8 __asm("_binary_font_portfolio_6x8_portfolio_6x8_data_start");
template <typename T>
@ -26,11 +28,21 @@ inline void copy(T * dst, const T * src, const int32_t n) noexcept
}
}
static uint8_t * framebuffer;
static uint8_t * framebuffer[2];
static char textbuffer[2][4 * 8];
static uint32_t send_buf[1024 / 4] __attribute__((aligned(32)));
static uint32_t recv_buf[1024 / 4] __attribute__((aligned(32)));
struct serial_error_counter {
uint32_t brk;
uint32_t er;
uint32_t dr;
uint32_t orer;
};
struct serial_error_counter error_counter;
enum struct step {
IDLE = 0,
DEVICE_STATUS,
@ -67,9 +79,13 @@ void send_vmu_framebuffer(maple::host_command_writer& writer, uint8_t port, uint
data_fields.phase = 0;
data_fields.block_number = std::byteswap<uint16_t>(0x0000);
copy<uint8_t>(data_fields.written_data,
reinterpret_cast<uint8_t *>(framebuffer),
maple::display::vmu::framebuffer_size);
uint8_t * fb;
if (lm == ap::lm_bus::_0)
fb = framebuffer[0 ^ (port & 1)];
else
fb = framebuffer[1 ^ (port & 1)];
copy<uint8_t>(data_fields.written_data, fb, maple::display::vmu::framebuffer_size);
}
void recv_extension_device_status(struct maple_display_poll_state &state)
@ -213,7 +229,7 @@ void handle_maple(struct maple_display_poll_state& state)
}
}
void render_glyphs(maple::display::font_renderer& renderer, char * s)
void render_glyphs(maple::display::font_renderer& renderer, const char * s)
{
for (int i = 0; i < 8 * 4; i++) {
int x = i % 8;
@ -222,23 +238,117 @@ void render_glyphs(maple::display::font_renderer& renderer, char * s)
}
}
void render_serial_state(maple::display::font_renderer& renderer, char * s, const char * msg)
void render_str(const char * src, char * dst)
{
int i = 0;
char c;
while ((c = src[i]) != 0) {
dst[i] = c;
i++;
}
}
void render_line(const char * src, char * dst, int src_len = 8)
{
bool end = false;
for (int i = 0; i < 8; i++) {
if (end || msg[i] == 0) {
s[0 + i] = ' ';
if (end || src[i] == 0 || i >= src_len) {
dst[i] = ' ';
end = true;
} else {
s[0 + i] = msg[i];
dst[i] = src[i];
}
}
}
void render_clear(char * dst, int len)
{
for (int i = 0; i < len; i++)
dst[i] = ' ';
}
void render_u32(uint32_t src, char * dst)
{
char num_buf[8];
string::hex(num_buf, 8, sh7091.SCIF.SCFSR2);
for (int i = 0; i < 8; i++) s[8 + i] = num_buf[i];
string::hex(num_buf, 8, sh7091.SCIF.SCFDR2);
for (int i = 0; i < 8; i++) s[16 + i] = num_buf[i];
render_glyphs(renderer, s);
string::hex(num_buf, 8, src);
for (int i = 0; i < 8; i++) dst[i] = num_buf[i];
}
void render_u8(uint32_t src, char * dst)
{
char num_buf[2];
string::hex(num_buf, 2, src);
for (int i = 0; i < 2; i++) dst[i] = num_buf[i];
}
void render_idle_state(char * dst)
{
render_line("idle", &dst[0]);
render_u32(serial_load::state.buf.u32[0], &dst[8]);
render_u32(serial_load::state.buf.u32[1], &dst[16]);
render_u8(serial_load::state.len, &dst[24]);
render_clear(&dst[26], 6);
}
void render_write_state(char * dst)
{
render_line("write", &dst[0]);
render_u32(sh7091.DMAC.DMATCR1, &dst[8]);
render_u32(sh7091.DMAC.DAR1, &dst[16]);
render_u32(serial_load::state.write_crc.value, &dst[24]);
}
void render_fsm_state(char * dst)
{
using namespace serial_load;
switch (state.fsm_state) {
case fsm_state::idle:
render_idle_state(dst);
break;
case fsm_state::write:
render_write_state(dst);
break;
case fsm_state::read:
render_line("read", &dst[0]);
render_clear(&dst[8], 24);
break;
case fsm_state::jump:
render_line("jump", &dst[0]);
render_clear(&dst[8], 24);
break;
case fsm_state::speed:
render_line("speed", &dst[0]);
render_clear(&dst[8], 24);
break;
default:
render_line("invalid", &dst[0]);
render_clear(&dst[8], 24);
break;
}
}
void render_error_counter_state(char * dst)
{
render_str("br", &dst[0]);
render_u8(error_counter.brk, &dst[2]);
render_str("er", &dst[4]);
render_u8(error_counter.er, &dst[6]);
render_str("dr", &dst[8]);
render_u8(error_counter.dr, &dst[10]);
render_str("oe", &dst[12]);
render_u8(error_counter.orer, &dst[14]);
render_u32(sh7091.SCIF.SCFSR2, &dst[16]);
render_u32(sh7091.SCIF.SCLSR2, &dst[24]);
}
void render(maple::display::font_renderer& renderer0,
maple::display::font_renderer& renderer1)
{
render_fsm_state(textbuffer[0]);
render_error_counter_state(textbuffer[1]);
render_glyphs(renderer0, textbuffer[0]);
render_glyphs(renderer1, textbuffer[1]);
}
void main() __attribute__((section(".text.main")));
@ -249,16 +359,12 @@ void main()
struct maple_display_poll_state state = {0};
const uint8_t * font = reinterpret_cast<const uint8_t *>(&_binary_font_portfolio_6x8);
auto renderer = maple::display::font_renderer(font);
framebuffer = renderer.fb;
char s[33] =
"1562500 " // 0
" " // 8
" " // 16
" "; // 24
render_glyphs(renderer, s);
state.want_start = 1;
auto renderer0 = maple::display::font_renderer(font);
framebuffer[0] = renderer0.fb;
auto renderer1 = maple::display::font_renderer(font);
framebuffer[1] = renderer1.fb;
state.want_start = 1;
serial_load::init();
// reset serial status
@ -266,35 +372,57 @@ void main()
// reset line status
sh7091.SCIF.SCLSR2 = 0;
uint32_t last_scfsr2 = -1;
uint32_t last_sclsr2 = -1;
serial::string("ready\n");
error_counter.brk = 0;
error_counter.er = 0;
error_counter.dr = 0;
error_counter.orer = 0;
while (1) {
using namespace scif;
const uint16_t scfsr2 = sh7091.SCIF.SCFSR2;
const uint16_t sclsr2 = sh7091.SCIF.SCLSR2;
if (scfsr2 & scfsr2::brk::bit_mask) {
render_serial_state(renderer, s, "brk");
// clear framing error and break
error_counter.brk += 1;
error_counter.er += (scfsr2 & scfsr2::er::bit_mask) != 0;
sh7091.SCIF.SCFSR2 = scfsr2 & ~(scfsr2::brk::bit_mask | scfsr2::er::bit_mask);
serial_load::init();
serial::reset_txrx();
} else if (scfsr2 & scfsr2::er::bit_mask) {
render_serial_state(renderer, s, "er");
// clear framing error
error_counter.er += 1;
sh7091.SCIF.SCFSR2 = scfsr2 & ~scfsr2::er::bit_mask;
} else if (scfsr2 & scfsr2::dr::bit_mask) {
render_serial_state(renderer, s, "dr");
} else if (sh7091.SCIF.SCLSR2 & sclsr2::orer::bit_mask) {
render_serial_state(renderer, s, "orer");
error_counter.dr += 1;
sh7091.SCIF.SCFSR2 = scfsr2 & ~scfsr2::dr::bit_mask;
} else if (sclsr2 & sclsr2::orer::bit_mask) {
error_counter.orer += 1;
sh7091.SCIF.SCLSR2 = scfsr2 & ~sclsr2::orer::bit_mask;
} else if (scfsr2 & scfsr2::rdf::bit_mask) {
render_serial_state(renderer, s, "rdf");
if (serial_load::state.fsm_state == serial_load::fsm_state::idle) {
while (sh7091.SCIF.SCFSR2 & scfsr2::rdf::bit_mask) {
const uint8_t c = sh7091.SCIF.SCFRDR2;
serial_load::recv(c);
} else {
render_serial_state(renderer, s, "idle");
sh7091.SCIF.SCFSR2 = scfsr2 & ~scfsr2::rdf::bit_mask;
}
}
}
serial_load::tick();
render(renderer0, renderer1);
//if (sclsr2 != last_sclsr2 || scfsr2 != last_scfsr2) {
state.want_start = 1;
//}
last_scfsr2 = scfsr2;
last_sclsr2 = sclsr2;
handle_maple(state);
uint16_t error_bits = scfsr2::er::bit_mask | scfsr2::brk::bit_mask;
if (sh7091.SCIF.SCFSR2 & error_bits) {
sh7091.SCIF.SCFSR2 = ~error_bits;
}
}
}

View File

@ -16,6 +16,8 @@
#include "holly/background.hpp"
#include "holly/region_array.hpp"
#include "sh7091/serial.hpp"
struct vertex {
float x;
float y;
@ -86,15 +88,15 @@ void init_texture_memory(const struct opb_size& opb_size)
background_parameter(0xff220000);
}
uint32_t _ta_parameter_buf[((32 + 64 + 32) + 32) / 4];
void main()
{
serial::init(4);
serial::string("main\n");
video_output::set_mode_vga();
// The address of `ta_parameter_buf` must be a multiple of 32 bytes.
// This is mandatory for ch2-dma to the ta fifo polygon converter.
uint32_t * ta_parameter_buf = align_32byte(_ta_parameter_buf);
uint32_t ta_parameter_buf[32 + 64 + 32 / 4] __attribute__((aligned(32)));
constexpr uint32_t ta_alloc = ta_alloc_ctrl::pt_opb::no_list
| ta_alloc_ctrl::tm_opb::no_list
@ -118,6 +120,8 @@ void main()
uint32_t frame_ix = 0;
serial::string("while true\n");
while (true) {
ta_polygon_converter_init(opb_size.total(),
ta_alloc,

View File

@ -116,8 +116,8 @@ void core_wait_end_of_render_video()
*/
while ((system.ISTNRM & istnrm::end_of_render_tsp) == 0) {
if (system.ISTERR) {
serial::string("core ");
serial::integer<uint32_t>(system.ISTERR);
//serial::string("core ");
//serial::integer<uint32_t>(system.ISTERR);
holly.SOFTRESET = softreset::pipeline_soft_reset;
holly.SOFTRESET = 0;
break;

View File

@ -142,8 +142,8 @@ void ta_wait_opaque_list()
{
while ((system.ISTNRM & istnrm::end_of_transferring_opaque_list) == 0) {
if (system.ISTERR) {
serial::string("ta ISTERR: ");
serial::integer<uint32_t>(system.ISTERR);
//serial::string("ta ISTERR: ");
//serial::integer<uint32_t>(system.ISTERR);
}
};

48
ip.lds
View File

@ -2,12 +2,14 @@ OUTPUT_FORMAT("elf32-shl", "elf32-shl", "elf32-shl")
MEMORY
{
systemid (arx) : ORIGIN = 0x8c008000, LENGTH = 0x100
toc (arx) : ORIGIN = 0x8c008100, LENGTH = 0x200
sg_sec (arx) : ORIGIN = 0x8c008300, LENGTH = 0x3400
sg_are (arx) : ORIGIN = 0x8c00b700, LENGTH = 0x100
sg_ini (arx) : ORIGIN = 0x8c00b800, LENGTH = 0x2800
aip (arx) : ORIGIN = 0x8c00e000, LENGTH = 0x2000
systemid : ORIGIN = 0x8c008000, LENGTH = 0x100
toc : ORIGIN = 0x8c008100, LENGTH = 0x200
sg_sec : ORIGIN = 0x8c008300, LENGTH = 0x3400
sg_are : ORIGIN = 0x8c00b700, LENGTH = 0x100
sg_ini : ORIGIN = 0x8c00b800, LENGTH = 0x4800
/* sg_ini : ORIGIN = 0x8c00b800, LENGTH = 0x2800 */
/* aip : ORIGIN = 0x8c00e000, LENGTH = 0x2000 */
p1ram : ORIGIN = 0x8c010000, LENGTH = 0xff0000
}
SECTIONS
@ -42,14 +44,22 @@ SECTIONS
.text.sg_ini :
{
KEEP(*(.text.*sg_ini))
KEEP(*(.text.start))
*(.text.startup.*)
*(.text*)
*(.rodata*)
*(.bss*)
*(.data*)
. = ORIGIN(sg_ini) + LENGTH(sg_ini);
} > sg_ini
/*
.text.aip :
{
KEEP(*(.text.*aip))
. = ORIGIN(aip) + LENGTH(aip);
} > aip
*/
/DISCARD/ :
{
@ -61,3 +71,29 @@ SECTIONS
INCLUDE "debug.lds"
}
__stack_reservation = 0x1000;
__stack_end = ORIGIN(p1ram) + LENGTH(p1ram) - __stack_reservation;
__text_link_start = 0;
__text_link_end = 0;
__text_load_start = 0;
__data_link_start = 0;
__data_link_end = 0;
__data_load_start = 0;
__rodata_link_start = 0;
__rodata_link_end = 0;
__rodata_load_start = 0;
__ctors_link_start = 0;
__ctors_link_end = 0;
__bss_link_start = 0;
__bss_link_end = 0;
__vbr_link_start = 0;
__vbr_link_end = 0;
__vbr_load_end = 0;
INCLUDE "addresses.lds"

View File

@ -68,6 +68,8 @@ SECTIONS
INCLUDE "debug.lds"
}
__stack_reservation = 0x0;
INCLUDE "symbols.lds"
INCLUDE "addresses.lds"
__stack_end = 0x8c008000;

View File

@ -4,5 +4,7 @@ MEMORY
p1ram : ORIGIN = 0x8c010000, LENGTH = 0xff0000
p2ram : ORIGIN = 0xac010000, LENGTH = 0xff0000
}
__stack_reservation = 0x1000;
INCLUDE "common.lds"
__stack_end = ORIGIN(p1ram) + LENGTH(p1ram) - 0x2000;

View File

@ -1,7 +1,10 @@
#include <cstdint>
#include "sh7091/serial.hpp"
#include "sh7091/serial_dma.hpp"
#include "serial_load.hpp"
#include "crc32.h"
namespace serial_load {
@ -23,10 +26,11 @@ static void move(void *dst, const void *src, uint32_t n)
void init()
{
state.len = 0;
state.command = CMD_NONE;
state.fsm_state = fsm_state::idle;
sh7091.DMAC.CHCR1 = 0;
}
static void jump_to_func(const uint32_t addr)
void jump_to_func(const uint32_t addr)
{
serial::string("jump to: ");
serial::integer<uint32_t>(addr);
@ -44,87 +48,117 @@ static void jump_to_func(const uint32_t addr)
// restore our stack
}
static inline void prestart_write()
{
uint32_t dest = state.buf.arg[0];
uint32_t size = state.buf.arg[1];
serial::recv_dma(dest - 1, size + 1);
state.write_crc.value = 0xffffffff;
state.write_crc.offset = dest;
}
struct state_arglen_reply command_list[] = {
{command::write, reply::write, fsm_state::write},
{command::read , reply::read , fsm_state::read },
{command::jump , reply::jump , fsm_state::jump },
{command::speed, reply::speed, fsm_state::speed},
};
constexpr uint32_t command_list_length = (sizeof (command_list)) / (sizeof (command_list[0]));
void recv(uint8_t c)
{
while (1) {
switch (state.command) {
case CMD_NONE:
state.buf[state.len++] = c;
if (state.len >= 4) {
if (state.buf[0] == 'D' &&
state.buf[1] == 'A' &&
state.buf[2] == 'T' &&
state.buf[3] == 'A') {
if (state.len < 12) {
state.buf.u8[state.len++] = c;
switch (state.fsm_state) {
case fsm_state::idle:
if (state.len == 16) {
for (uint32_t i = 0; i < command_list_length; i++) {
struct state_arglen_reply& sar = command_list[i];
if (state.buf.cmd == sar.command) {
uint32_t crc = crc32(&state.buf.u8[0], 12);
if (crc == state.buf.crc) {
// valid command, do the transition
if (state.buf.cmd == command::write) prestart_write();
state.fsm_state = sar.fsm_state;
state.len = 0;
union command_reply reply = command_reply(sar.reply, state.buf.arg[0], state.buf.arg[1]);
serial::string(reply.u8, 16);
return;
} else {
serial::string("data");
state.command = CMD_DATA;
/*
union command_reply reply = crc_error_reply(crc);
serial::string(reply.u8, 16);
state.len = 0;
return;
*/
}
} else if (state.buf[0] == 'J' &&
state.buf[1] == 'U' &&
state.buf[2] == 'M' &&
state.buf[3] == 'P') {
if (state.len < 8) {
return;
} else {
serial::string("jump");
state.command = CMD_JUMP;
}
} else if (state.buf[0] == 'R' &&
state.buf[1] == 'A' &&
state.buf[2] == 'T' &&
state.buf[3] == 'E') {
if (state.len < 8) {
return;
} else {
serial::string("rate");
state.command = CMD_RATE;
}
} else {
move(&state.buf[0], &state.buf[1], state.len - 1);
// invalid command
move(&state.buf.u8[0], &state.buf.u8[1], state.len - 1);
state.len -= 1;
}
} else {
return;
}
break;
case CMD_DATA:
}
default:
break;
}
}
void tick()
{
uint32_t * size = &state.addr1;
uint8_t * dest = reinterpret_cast<uint8_t *>(state.addr2);
if (*size > 0) {
serial::character(c);
switch (state.fsm_state) {
case fsm_state::idle:
break;
case fsm_state::write:
{
// read chcr1 before dar1 to avoid race
uint32_t chcr1 = sh7091.DMAC.CHCR1;
uint32_t dar1 = sh7091.DMAC.DAR1;
if (dar1 > state.write_crc.offset) {
uint32_t len = dar1 - state.write_crc.offset;
const uint8_t * dest = reinterpret_cast<const uint8_t *>(state.write_crc.offset);
state.write_crc.value = crc32_update(state.write_crc.value, dest, len);
state.write_crc.offset += len;
}
if (chcr1 & dmac::chcr::te::transfers_completed) {
state.write_crc.value ^= 0xffffffff;
union command_reply reply = write_crc_reply(state.write_crc.value);
serial::string(reply.u8, 16);
// write c to dest
*dest = c;
state.addr2++;
sh7091.DMAC.CHCR1 = 0;
(*size)--;
// transition to next state
state.fsm_state = fsm_state::idle;
}
if (*size == 0) {
state.len = 0;
state.command = CMD_NONE;
serial::string("next");
}
return;
break;
}
case CMD_JUMP:
// jump
state.len = 0;
state.command = CMD_NONE;
jump_to_func(state.addr1);
return;
case fsm_state::read:
break;
case CMD_RATE:
state.len = 0;
state.command = CMD_NONE;
serial::init(state.addr1 & 0xff);
case fsm_state::jump:
{
using namespace scif;
// wait for serial transmission to end
constexpr uint32_t transmission_end = scfsr2::tend::bit_mask | scfsr2::tdfe::bit_mask;
if ((sh7091.SCIF.SCFSR2 & transmission_end) != transmission_end)
return;
break;
const uint32_t dest = state.buf.arg[0];
jump_to_func(dest);
// transition to next state
state.fsm_state = fsm_state::idle;
}
break;
case fsm_state::speed:
{
const uint32_t speed = state.buf.arg[0];
serial::init(speed & 0xff);
// transition to next state
state.fsm_state = fsm_state::idle;
}
break;
default:
break;
}
}

View File

@ -1,28 +1,40 @@
#pragma once
#include <cstdint>
#include "serial_protocol.hpp"
namespace serial_load {
void init();
void recv(uint8_t c);
void tick();
enum command {
CMD_NONE,
CMD_DATA, // DATA 0000 0000 {data}
CMD_JUMP, // JUMP 0000
CMD_RATE, // RATE 0000
enum struct fsm_state {
idle,
write,
read,
jump,
speed,
};
struct state_arglen_reply {
uint32_t command;
uint32_t reply;
enum fsm_state fsm_state;
};
struct incremental_crc {
uint32_t value;
uint32_t offset;
};
struct state {
union {
uint8_t buf[12];
struct {
uint8_t cmd[4];
uint32_t addr1;
uint32_t addr2;
};
};
union command_reply buf;
uint32_t len;
enum command command;
enum fsm_state fsm_state;
struct incremental_crc write_crc;
struct incremental_crc read_crc;
};
extern struct state state;

124
serial_protocol.hpp Normal file
View File

@ -0,0 +1,124 @@
#pragma once
#include <stdint.h>
#include "crc32.h"
namespace serial_load {
consteval uint32_t gen_cmd(const char * s)
{
uint32_t x = 0
| s[0] << 0
| s[1] << 8
| s[2] << 16
| s[3] << 24;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return x;
}
namespace command {
constexpr uint32_t write = gen_cmd("WRTE");
constexpr uint32_t read = gen_cmd("READ");
constexpr uint32_t jump = gen_cmd("JUMP");
constexpr uint32_t speed = gen_cmd("SPED");
static_assert(write == 0x2cc46ed8);
static_assert(read == 0xf18d57c7);
static_assert(jump == 0xa6696f38);
static_assert(speed == 0x27a7a9f4);
}
namespace reply {
constexpr uint32_t write = gen_cmd("wrte");
constexpr uint32_t read = gen_cmd("read");
constexpr uint32_t jump = gen_cmd("jump");
constexpr uint32_t speed = gen_cmd("sped");
constexpr uint32_t write_crc = gen_cmd("crcW");
static_assert(write == 0x8c661aaa);
static_assert(read == 0x512f23b5);
static_assert(jump == 0x06cb1b4a);
static_assert(speed == 0x8705dd86);
static_assert(write_crc == 0x1cced074);
}
union command_reply {
uint8_t u8[16];
uint32_t u32[4];
struct {
uint32_t cmd;
uint32_t arg[2];
uint32_t crc;
};
};
static inline uint32_t le_bswap(const uint32_t n)
{
if constexpr (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
return n;
else
return __builtin_bswap32(n);
}
constexpr union command_reply command_reply(uint32_t cmd, uint32_t arg0, uint32_t arg1)
{
union command_reply command = {
.cmd = le_bswap(cmd),
.arg = { le_bswap(arg0), le_bswap(arg1) },
.crc = 0,
};
command.crc = le_bswap(crc32(command.u8, 12));
return command;
}
constexpr union command_reply write_command(uint32_t dest, uint32_t size)
{
return command_reply(command::write, dest, size);
}
constexpr union command_reply read_command(uint32_t dest, uint32_t size)
{
return command_reply(command::read, dest, size);
}
constexpr union command_reply jump_command(uint32_t dest)
{
return command_reply(command::jump, dest, 0);
}
constexpr union command_reply speed_command(uint32_t speed)
{
return command_reply(command::speed, speed, 0);
}
constexpr union command_reply write_reply(uint32_t dest, uint32_t size)
{
return command_reply(reply::write, dest, size);
}
constexpr union command_reply read_reply(uint32_t dest, uint32_t size)
{
return command_reply(reply::read, dest, size);
}
constexpr union command_reply jump_reply(uint32_t dest)
{
return command_reply(reply::jump, dest, 0);
}
constexpr union command_reply speed_reply(uint32_t speed)
{
return command_reply(reply::speed, speed, 0);
}
constexpr union command_reply write_crc_reply(uint32_t crc)
{
return command_reply(reply::write_crc, crc, 0);
}
}

View File

@ -24,6 +24,19 @@ static inline void init_wait()
sh7091.TMU.TSTR &= (~tmu::tstr::str1::counter_start) & 0xff; // stop TCNT1
}
void reset_txrx()
{
using namespace scif;
sh7091.SCIF.SCFCR2 |= ( scfcr2::tfrst::reset_operation_enabled
| scfcr2::rfrst::reset_operation_enabled
);
sh7091.SCIF.SCFCR2 &= ~( scfcr2::tfrst::reset_operation_enabled
| scfcr2::rfrst::reset_operation_enabled
);
}
void init(uint8_t bit_rate)
{
using namespace scif;

View File

@ -4,6 +4,8 @@
namespace serial {
void reset_txrx();
void init(uint8_t bit_rate);
void character(const char c);

View File

@ -1,5 +1,6 @@
#pragma once
#include "sh7091/sh7091.hpp"
#include "sh7091/sh7091_bits.hpp"
/*
@ -42,7 +43,7 @@
namespace serial {
static void recv_dma(void * destination_address, uint32_t length)
static void recv_dma(uint32_t destination_address, uint32_t length)
{
using namespace dmac;
/* Initial settings (SAR, DAR, DMATCR, CHCR, DMAOR) */
@ -53,13 +54,10 @@ static void recv_dma(void * destination_address, uint32_t length)
// SAR1 = SCFRDR2
// DAR1 = (address)
// DMATCR1 = (transfer count, 24 bit)
sh7091.DMAC.CHCR3 = 0;
sh7091.DMAC.CHCR2 = 0;
sh7091.DMAC.CHCR1 = 0;
sh7091.DMAC.CHCR0 = 0;
sh7091.DMAC.SAR1 = reinterpret_cast<uint32_t>(&sh7091.SCIF.SCFRDR2);
sh7091.DMAC.DAR1 = reinterpret_cast<uint32_t>(destination_address);
sh7091.DMAC.DAR1 = destination_address;
sh7091.DMAC.DMATCR1 = length & 0x00ff'ffff;
// SSA (only used for PCMCIA)

View File

@ -1,6 +1,3 @@
/* reserve bytes for serial program loader */
__stack_end = ORIGIN(p1ram) + LENGTH(p1ram) - __stack_reservation;
__text_link_start = ADDR(.text);
__text_link_end = ADDR(.text) + SIZEOF(.text);
__text_load_start = LOADADDR(.text);

View File

@ -4,21 +4,24 @@ CXXFLAGS = -std=c++23
FREETYPE_CFLAGS = $(shell pkg-config --cflags freetype2)
FREETYPE_LDFLAGS = $(shell pkg-config --libs freetype2)
FTDI_CFLAGS = $(shell pkg-config --cflags libftdi1)
FTDI_CFLAGS = $(shell pkg-config --cflags libftdi1) -I..
FTDI_LDFLAGS = $(shell pkg-config --libs libftdi1)
all: ttf_outline
crc32.o: ../crc32.c
$(CC) -std=gnu2x $(CFLAGS) -I.. -c $< -o $@
ttf_%.o: ttf_%.cpp
$(CXX) $(CFLAGS) $(FREETYPE_CFLAGS) $(CXXFLAGS) -c $< -o $@
$(CXX) $(CFLAGS) $(CXXFLAGS) $(FREETYPE_CFLAGS) -c $< -o $@
ttf_%: ttf_%.o
$(CXX) $(LDFLAGS) $(FREETYPE_LDFLAGS) $^ -o $@
ftdi_%.o: ftdi_%.c
$(CC) -std=gnu2x $(CFLAGS) $(FTDI_CFLAGS) -c $< -o $@
ftdi_%.o: ftdi_%.cpp
$(CXX) $(CFLAGS) $(CXXFLAGS) $(FTDI_CFLAGS) -c $< -o $@
ftdi_%: ftdi_%.o
ftdi_%: ftdi_%.o crc32.o
$(CXX) $(LDFLAGS) $(FTDI_LDFLAGS) $^ -o $@
ttf_outline: ttf_outline.o 2d_pack.o

37
tools/command_gen.py Normal file
View File

@ -0,0 +1,37 @@
def gen_cmd(s):
x = (0
| s[0] << 0
| s[1] << 8
| s[2] << 16
| s[3] << 24
)
x ^= (x << 13) & 0xffff_ffff
x ^= (x >> 17) & 0xffff_ffff
x ^= (x << 5) & 0xffff_ffff
return x
commands = [
"WRTE",
"READ",
"JUMP",
"SPED",
]
replies = [
"wrte",
"read",
"jump",
"sped",
"crcW",
]
for c in commands:
x = gen_cmd(c.encode('ascii'))
print(c, f"{x:08x}")
for c in replies:
x = gen_cmd(c.encode('ascii'))
print(c, f"{x:08x}")

View File

@ -9,23 +9,26 @@
#include <ftdi.h>
#include <libusb.h>
#include "crc32.h"
#include "serial_protocol.hpp"
extern int convert_baudrate_UT_export(int baudrate, struct ftdi_context *ftdi,
unsigned short *value, unsigned short *index);
int dreamcast_rates[] = {
1562500,
781250,
520833,
390625,
312500,
260416,
223214,
195312,
173611,
156250,
142045,
130208,
120192
1562500, // 0
781250, // 1
520833, // 2
390625, // 3
312500, // 4
260416, // 5
223214, // 6
195312, // 7
173611, // 8
156250, // 9
142045, // 10
130208, // 11
120192 // 12
};
int init_ftdi_context(struct ftdi_context * ftdi)
@ -63,9 +66,9 @@ int init_ftdi_context(struct ftdi_context * ftdi)
}
ftdi_list_free(&devlist);
/*
unsigned short value;
unsigned short index;
for (unsigned int i = 0; i < (sizeof (dreamcast_rates)) / (sizeof (dreamcast_rates[0])); i++) {
int baud = convert_baudrate_UT_export(dreamcast_rates[i], ftdi, &value, &index);
float baudf = baud;
@ -73,15 +76,21 @@ int init_ftdi_context(struct ftdi_context * ftdi)
float error = (baudf > ratef) ? ratef / baudf : baudf / ratef;
fprintf(stdout, "%d: best: %d, error: %f\n", dreamcast_rates[i], baud, (1.f - error) * 100.f);
}
*/
res = ftdi_set_baudrate(ftdi, 1562500);
//res = ftdi_set_baudrate(ftdi, 312500);
res = ftdi_set_baudrate(ftdi, dreamcast_rates[0]);
if (res < 0) {
fprintf(stderr, "ftdi_set_baudrate\n");
return -1;
}
res = ftdi_set_line_property(ftdi, 8, STOP_BIT_1, NONE);
res = ftdi_set_line_property2(ftdi, BITS_8, STOP_BIT_1, NONE, BREAK_ON);
if (res < 0) {
fprintf(stderr, "ftdi_set_line_property\n");
return -1;
}
res = ftdi_set_line_property2(ftdi, BITS_8, STOP_BIT_1, NONE, BREAK_OFF);
if (res < 0) {
fprintf(stderr, "ftdi_set_line_property\n");
return -1;
@ -117,15 +126,7 @@ union data_command {
};
uint8_t data[4 * 3];
};
static_assert((sizeof (union data_command)) == 4 * 3);
uint32_t bswap(const uint32_t n)
{
if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
return n;
else
return __builtin_bswap32(n);
}
static_assert((sizeof (union data_command)) == 12);
long read_with_timeout(struct ftdi_context * ftdi, uint8_t * read_buf, const long expect_length)
{
@ -167,79 +168,110 @@ long max(long a, long b)
return a > b ? a : b;
}
void symmetric(struct ftdi_context * ftdi, const uint8_t * tx_buf, const long size)
{
int res;
uint8_t rx_buf[size];
long tx_offset = 0;
long rx_offset = 0;
while (tx_offset < size) {
long txrx_diff = tx_offset - rx_offset;
long tx_length = max(min(min(chunk_size, size - tx_offset), chunk_size - txrx_diff), 0);
if (tx_length > 0) {
res = ftdi_write_data(ftdi, &tx_buf[tx_offset], tx_length);
assert(res >= 0);
tx_offset += res;
}
res = ftdi_read_data(ftdi, &rx_buf[rx_offset], size - rx_offset);
assert(res >= 0);
rx_offset += res;
}
for (int i = 0; i < size; i++) {
if (tx_buf[i] != rx_buf[i]) {
fprintf(stderr, "mismatch at %d\n", i);
return;
}
}
fprintf(stderr, "equal\n");
}
double timespec_difference(struct timespec const * const a, struct timespec const * const b)
{
return (double)(a->tv_sec - b->tv_sec) + (double)(a->tv_nsec - b->tv_nsec) / 1'000'000'000.0;
}
int transfer(struct ftdi_context * ftdi, const uint8_t * buf, const long size)
void dump_command_reply(union serial_load::command_reply& cr)
{
for (uint32_t i = 0; i < (sizeof (union serial_load::command_reply)) / (sizeof (uint32_t)); i++) {
fprintf(stderr, " %08x\n", cr.u32[i]);
}
}
int read_reply(struct ftdi_context * ftdi, uint32_t expected_cmd, union serial_load::command_reply& reply)
{
using namespace serial_load;
constexpr long read_length = (sizeof (union serial_load::command_reply));
long length = read_with_timeout(ftdi, reply.u8, read_length);
if (length != read_length) {
fprintf(stderr, "short read; want %ld bytes; received: %ld\n", read_length, length);
return -1;
}
for (uint32_t i = 0; i < (sizeof (reply)) / (sizeof (uint32_t)); i++) {
reply.u32[i] = le_bswap(reply.u32[i]);
}
uint32_t crc = crc32(&reply.u8[0], 12);
if (crc != reply.crc) {
fprintf(stderr, "crc mismatch; remote crc: %08x; local crc: %08x\n", reply.crc, crc);
dump_command_reply(reply);
return -1;
}
if (reply.cmd != expected_cmd) {
fprintf(stderr, "invalid reply; remote cmd %08x; expected cmd: %08x\n", reply.cmd, expected_cmd);
dump_command_reply(reply);
return -1;
}
return 0;
}
int do_write(struct ftdi_context * ftdi, const uint8_t * buf, const uint32_t size)
{
int res;
union data_command command = {
.command = {'D', 'A', 'T', 'A'},
.size = bswap(size),
.dest = bswap(0xac010000),
};
res = ftdi_write_data(ftdi, command.data, (sizeof (union data_command)));
assert(res >= 0);
const char * expect = "data\n";
const long expect_length = 5;
uint8_t read_buf[expect_length + 1];
read_buf[expect_length] = 0;
long read_length = read_with_timeout(ftdi, read_buf, expect_length);
if (read_length != expect_length) {
fprintf(stderr, "want %ld bytes; received: %ld\n", expect_length, read_length);
return -1;
}
res = memcmp(read_buf, expect, expect_length);
const uint32_t dest = 0xac010000;
union serial_load::command_reply command = serial_load::write_command(dest, size);
res = ftdi_write_data(ftdi, command.u8, (sizeof (command)));
assert(res == (sizeof (command)));
union serial_load::command_reply reply;
res = read_reply(ftdi, serial_load::reply::write, reply);
if (res != 0) {
fprintf(stderr, "expect `%s`; received: `%s`\n", expect, read_buf);
return -1;
}
fprintf(stderr, "OK\n");
fprintf(stderr, "remote: dest: %08x size: %08x\n", reply.arg[0], reply.arg[1]);
if (reply.arg[0] != dest || reply.arg[1] != size) {
return -1;
}
struct timespec start;
struct timespec end;
res = clock_gettime(CLOCK_MONOTONIC, &start);
symmetric(ftdi, buf, size);
assert(res == 0);
res = ftdi_write_data(ftdi, buf, size);
assert(res >= 0);
assert((uint32_t)res == size);
res = clock_gettime(CLOCK_MONOTONIC, &end);
assert(res == 0);
fprintf(stderr, "symmetric time: %.03f\n", timespec_difference(&end, &start));
uint32_t buf_crc = crc32(buf, size);
union serial_load::command_reply crc_reply;
res = read_reply(ftdi, serial_load::reply::write_crc, crc_reply);
if (res != 0) {
return -1;
}
fprintf(stderr, "remote crc: %08x; local crc %08x\n", crc_reply.arg[0], buf_crc);
return 0;
}
int do_jump(struct ftdi_context * ftdi)
{
int res;
const uint32_t dest = 0xac010000;
union serial_load::command_reply command = serial_load::jump_command(dest);
res = ftdi_write_data(ftdi, command.u8, (sizeof (command)));
assert(res == (sizeof (command)));
union serial_load::command_reply reply;
res = read_reply(ftdi, serial_load::reply::jump, reply);
if (res != 0) {
return -1;
}
fprintf(stderr, "remote: jump: %08x\n", reply.arg[0]);
if (reply.arg[0] != dest || reply.arg[1] != 0) {
return -1;
}
return 0;
}
@ -273,12 +305,14 @@ int main(int argc, char * argv[])
}
fprintf(stderr, "%s off %ld\n", argv[1], off);
uint8_t buf[off];
//uint8_t buf[off];
uint8_t * buf = (uint8_t *)malloc(off);
ssize_t size = fread(buf, 1, off, file);
if (size < 0) {
fprintf(stderr, "read");
return EXIT_FAILURE;
}
printf("%02x\n", buf[0]);
ret = fclose(file);
if (ret < 0) {
@ -300,13 +334,25 @@ int main(int argc, char * argv[])
return EXIT_FAILURE;
}
int return_code = EXIT_SUCCESS;
struct timespec start;
struct timespec end;
res = clock_gettime(CLOCK_MONOTONIC, &start);
int transfer_ret = transfer(ftdi, buf, off);
assert(res >= 0);
int do_write_ret = do_write(ftdi, buf, off);
res = clock_gettime(CLOCK_MONOTONIC, &end);
fprintf(stderr, "time: %.03f\n", timespec_difference(&end, &start));
return transfer_ret;
assert(res >= 0);
fprintf(stderr, "do_write time: %.03f\n", timespec_difference(&end, &start));
if (do_write_ret == 0) {
do_jump(ftdi);
} else {
return_code = EXIT_FAILURE;
}
res = clock_gettime(CLOCK_MONOTONIC, &end);
assert(res >= 0);
fprintf(stderr, "total time: %.03f\n", timespec_difference(&end, &start));
return return_code;
}