serial_transfer: add read command

This commit is contained in:
Zack Buhman 2024-10-26 08:03:50 -05:00
parent ff7a03a75c
commit 45c6bcf211
13 changed files with 200 additions and 72 deletions

View File

@ -9,6 +9,7 @@ CFLAGS += -Wno-array-bounds
CFLAGS += -Wno-error=maybe-uninitialized
CFLAGS += -Wno-error=unused-but-set-variable
CFLAGS += -Wno-error=unused-variable
CFLAGS += -Wno-error=unused-function
CXXFLAGS += -fno-exceptions -fno-non-call-exceptions -fno-rtti -fno-threadsafe-statics

View File

@ -72,11 +72,11 @@ const uint32_t crc32_table[256] = {
};
uint32_t
crc32_update(uint32_t crc, const uint8_t * data, uint32_t len)
crc32_update(uint32_t crc, const uint8_t * buf, uint32_t len)
{
while (len > 0) {
crc = crc32_table[*data ^ ((crc >> 24) & 0xff)] ^ (crc << 8);
data++;
crc = crc32_table[*buf ^ ((crc >> 24) & 0xff)] ^ (crc << 8);
buf++;
len--;
}

View File

@ -181,8 +181,5 @@ void main()
theta += half_degree;
frame_ix = (frame_ix + 1) & 1;
frame += 1;
if (frame > 10)
break;
}
}

View File

@ -173,7 +173,7 @@ void recv_device_status(struct maple_display_poll_state &state)
if (bus_data.command_code != maple::device_status::command_code) {
state.port[port].ap__lm = 0;
} else {
auto& data_fields = bus_data.data_fields;
//auto& data_fields = bus_data.data_fields;
uint8_t lm = bus_data.source_ap & ap::lm_bus::bit_mask;
state.port[port].ap__lm = lm;
@ -298,6 +298,14 @@ void render_write_state(char * dst)
render_u32(serial_load::state.write_crc.value, &dst[24]);
}
void render_read_state(char * dst)
{
render_line("read", &dst[0]);
render_u32(sh7091.DMAC.DMATCR1, &dst[8]);
render_u32(sh7091.DMAC.SAR1, &dst[16]);
render_u32(serial_load::state.read_crc.value, &dst[24]);
}
void render_fsm_state(char * dst)
{
using namespace serial_load;
@ -309,8 +317,7 @@ void render_fsm_state(char * dst)
render_write_state(dst);
break;
case fsm_state::read:
render_line("read", &dst[0]);
render_clear(&dst[8], 24);
render_read_state(dst);
break;
case fsm_state::jump:
render_line("jump", &dst[0]);
@ -355,7 +362,8 @@ void main() __attribute__((section(".text.main")));
void main()
{
serial::init(0);
constexpr uint32_t serial_speed = 0;
serial_load::init(serial_speed);
struct maple_display_poll_state state = {0};
const uint8_t * font = reinterpret_cast<const uint8_t *>(&_binary_font_portfolio_6x8);
@ -364,17 +372,11 @@ void main()
auto renderer1 = maple::display::font_renderer(font);
framebuffer[1] = renderer1.fb;
state.want_start = 1;
serial_load::init();
// reset serial status
sh7091.SCIF.SCFSR2 = 0;
// 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;
@ -393,7 +395,7 @@ void main()
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_load::init(serial_load::state.speed);
serial::reset_txrx();
} else if (scfsr2 & scfsr2::er::bit_mask) {
// clear framing error
@ -418,11 +420,7 @@ void main()
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);
}
}

View File

@ -3,7 +3,7 @@ MEMORY
{
p1ram : ORIGIN = 0xac005000, LENGTH = 0x0000
p2ram : ORIGIN = 0xac010000, LENGTH = 0xff0000
ldram : ORIGIN = 0xacffe000, LENGTH = 0x2000
ldram : ORIGIN = 0xacffd000, LENGTH = 0x3000
}
SECTIONS
{

View File

@ -7,4 +7,4 @@ MEMORY
INCLUDE "common.lds"
__stack_end = ORIGIN(p1ram) + LENGTH(p1ram) - 0x2000;
__stack_end = ORIGIN(p1ram) + LENGTH(p1ram) - 0x3000;

View File

@ -23,11 +23,13 @@ static void move(void *dst, const void *src, uint32_t n)
}
}
void init()
void init(uint32_t speed)
{
state.len = 0;
state.fsm_state = fsm_state::idle;
state.speed = speed & 0xff;
sh7091.DMAC.CHCR1 = 0;
serial::init(state.speed);
}
void jump_to_func(const uint32_t addr)
@ -35,15 +37,16 @@ void jump_to_func(const uint32_t addr)
serial::string("jump to: ");
serial::integer<uint32_t>(addr);
// save our stack
asm volatile ("ldc r15, gbr; "
"mov #0, r15; "
asm volatile ("mov.l r14,@-r15; "
"ldc r15, gbr; "
"jsr @%0; "
"nop; "
"mov #0, r15; "
"stc gbr, r15; "
"mov.l @r15+,r14; "
:
: "r"(addr) /* input */
/* clobbered register */
: "r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10","r11","r12","macl","mach","gbr","pr"
: "r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10","r11","r12","r13","macl","mach","gbr","pr"
);
// restore our stack
}
@ -57,6 +60,15 @@ static inline void prestart_write()
state.write_crc.offset = dest;
}
static inline void prestart_read()
{
uint32_t src = state.buf.arg[0];
uint32_t size = state.buf.arg[1];
serial::send_dma(src, size);
state.read_crc.value = 0xffffffff;
state.read_crc.offset = src;
}
struct state_arglen_reply command_list[] = {
{command::write, reply::write, fsm_state::write},
{command::read , reply::read , fsm_state::read },
@ -82,6 +94,8 @@ void recv(uint8_t c)
state.len = 0;
union command_reply reply = command_reply(sar.reply, state.buf.arg[0], state.buf.arg[1]);
serial::string(reply.u8, 16);
if (state.buf.cmd == command::read) prestart_read();
return;
} else {
/*
@ -115,8 +129,8 @@ void tick()
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);
const uint8_t * buf = reinterpret_cast<const uint8_t *>(state.write_crc.offset);
state.write_crc.value = crc32_update(state.write_crc.value, buf, len);
state.write_crc.offset += len;
}
if (chcr1 & dmac::chcr::te::transfers_completed) {
@ -132,6 +146,27 @@ void tick()
}
break;
case fsm_state::read:
{
uint32_t chcr1 = sh7091.DMAC.CHCR1;
uint32_t sar1 = sh7091.DMAC.SAR1;
if (sar1 > state.read_crc.offset) {
uint32_t len = sar1 - state.read_crc.offset;
const uint8_t * buf = reinterpret_cast<const uint8_t *>(state.read_crc.offset);
state.read_crc.value = crc32_update(state.read_crc.value, buf, len);
state.read_crc.offset += len;
}
if (chcr1 & dmac::chcr::te::transfers_completed) {
state.read_crc.value ^= 0xffffffff;
union command_reply reply = read_crc_reply(state.read_crc.value);
serial::string(reply.u8, 16);
sh7091.DMAC.CHCR1 = 0;
// transition to next state
//state.fsm_state = fsm_state::idle;
}
}
break;
case fsm_state::jump:
{
@ -144,6 +179,11 @@ void tick()
const uint32_t dest = state.buf.arg[0];
jump_to_func(dest);
// cautiously re-initialize serial; it is possible the called
// function modified serial state
serial::init(state.speed);
sh7091.DMAC.CHCR1 = 0;
// transition to next state
state.fsm_state = fsm_state::idle;
}
@ -151,7 +191,8 @@ void tick()
case fsm_state::speed:
{
const uint32_t speed = state.buf.arg[0];
serial::init(speed & 0xff);
state.speed = speed & 0xff;
serial::init(state.speed);
// transition to next state
state.fsm_state = fsm_state::idle;

View File

@ -6,7 +6,7 @@
namespace serial_load {
void init();
void init(uint32_t speed);
void recv(uint8_t c);
void tick();
@ -35,6 +35,7 @@ struct state {
enum fsm_state fsm_state;
struct incremental_crc write_crc;
struct incremental_crc read_crc;
uint32_t speed;
};
extern struct state state;

View File

@ -39,12 +39,14 @@ namespace reply {
constexpr uint32_t jump = gen_cmd("jump");
constexpr uint32_t speed = gen_cmd("sped");
constexpr uint32_t write_crc = gen_cmd("crcW");
constexpr uint32_t read_crc = gen_cmd("crcR");
static_assert(write == 0x8c661aaa);
static_assert(read == 0x512f23b5);
static_assert(jump == 0x06cb1b4a);
static_assert(speed == 0x8705dd86);
static_assert(write_crc == 0x1cced074);
static_assert(read_crc == 0xb9ce82f4);
}
union command_reply {
@ -121,4 +123,9 @@ constexpr union command_reply write_crc_reply(uint32_t crc)
return command_reply(reply::write_crc, crc, 0);
}
constexpr union command_reply read_crc_reply(uint32_t crc)
{
return command_reply(reply::read_crc, crc, 0);
}
}

View File

@ -19,7 +19,7 @@ static inline void init_wait()
sh7091.TMU.TSTR |= tmu::tstr::str1::counter_start;
uint32_t start = sh7091.TMU.TCNT1;
while ((start - sh7091.TMU.TCNT1) < 1);
while ((start - sh7091.TMU.TCNT1) < 20);
sh7091.TMU.TSTR &= (~tmu::tstr::str1::counter_start) & 0xff; // stop TCNT1
}
@ -48,11 +48,6 @@ void init(uint8_t bit_rate)
sh7091.SCIF.SCFCR2 = scfcr2::tfrst::reset_operation_enabled
| scfcr2::rfrst::reset_operation_enabled;
sh7091.SCIF.SCFCR2 = scfcr2::rtrg::trigger_on_1_byte
| scfcr2::ttrg::trigger_on_8_bytes
//| scfcr2::mce::modem_signals_enabled
;
sh7091.SCIF.SCSMR2 = scsmr2::chr::_8_bit_data
| scsmr2::pe::parity_disabled
| scsmr2::stop::_1_stop_bit
@ -72,6 +67,11 @@ void init(uint8_t bit_rate)
// wait 1 bit interval
init_wait();
sh7091.SCIF.SCFCR2 = scfcr2::rtrg::trigger_on_1_byte
| scfcr2::ttrg::trigger_on_8_bytes
//| scfcr2::mce::modem_signals_enabled
;
sh7091.SCIF.SCSCR2 = scscr2::te::transmission_enabled
| scscr2::re::reception_enabled;
@ -84,7 +84,7 @@ void character(const char c)
// wait for transmit fifo to become partially empty
while ((sh7091.SCIF.SCFSR2 & scfsr2::tdfe::bit_mask) == 0);
sh7091.SCIF.SCFSR2 = static_cast<uint16_t>(~scfsr2::tdfe::bit_mask);
sh7091.SCIF.SCFSR2 &= ~scfsr2::tdfe::bit_mask;
sh7091.SCIF.SCFTDR2 = static_cast<uint8_t>(c);
}

View File

@ -78,7 +78,7 @@ static void recv_dma(uint32_t destination_address, uint32_t length)
// DE channel enable
sh7091.DMAC.CHCR1 = chcr::dm::destination_address_incremented
| chcr::sm::source_address_fixed
| chcr::rs::resource_select(0b1011) /* SCIF */
| chcr::rs::resource_select(0b1011) /* SCFRDR2 → external address space */
| chcr::tm::cycle_steal_mode /* transmit mode */
| chcr::ts::_8_bit /* transfer size */
//| chcr::ie::interrupt_request_generated
@ -89,4 +89,23 @@ static void recv_dma(uint32_t destination_address, uint32_t length)
| dmaor::dme::operation_enabled_on_all_channels; /* DMAC master enable */
}
static void send_dma(uint32_t source_address, uint32_t length)
{
using namespace dmac;
sh7091.DMAC.CHCR1 = 0;
sh7091.DMAC.SAR1 = source_address;
sh7091.DMAC.DAR1 = reinterpret_cast<uint32_t>(&sh7091.SCIF.SCFTDR2);
sh7091.DMAC.DMATCR1 = length & 0x00ff'ffff;
sh7091.DMAC.CHCR1 = chcr::dm::destination_address_fixed
| chcr::sm::source_address_incremented
| chcr::rs::resource_select(0b1010) /* external address space → SCFTDR2 */
| chcr::tm::cycle_steal_mode /* transmit mode */
| chcr::ts::_8_bit /* transfer size */
//| chcr::ie::interrupt_request_generated
| chcr::de::channel_operation_enabled;
}
}

View File

@ -25,6 +25,7 @@ replies = [
"jump",
"sped",
"crcW",
"crcR",
]
for c in commands:

View File

@ -176,7 +176,7 @@ double timespec_difference(struct timespec const * const a, struct timespec cons
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]);
fprintf(stderr, " %08x\n", serial_load::le_bswap(cr.u32[i]));
}
}
@ -192,15 +192,23 @@ int read_reply(struct ftdi_context * ftdi, uint32_t expected_cmd, union serial_l
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);
fprintf(stderr, "reply crc mismatch; remote crc: %08x; local crc: %08x\n", reply.crc, crc);
dump_command_reply(reply);
return -1;
/*
uint8_t buf[16] = {0};
long length = read_with_timeout(ftdi, reply.u8, 16);
if (length > 0) {
fprintf(stderr, "trailing data:\n");
for (int i = 0; i < length; i++) {
fprintf(stderr, "%02x ", buf[i]);
}
fprintf(stderr, "\n");
}
*/
return -2;
}
if (reply.cmd != expected_cmd) {
@ -208,12 +216,14 @@ int read_reply(struct ftdi_context * ftdi, uint32_t expected_cmd, union serial_l
dump_command_reply(reply);
return -1;
}
dump_command_reply(reply);
return 0;
}
int do_write(struct ftdi_context * ftdi, const uint8_t * buf, const uint32_t size)
{
fprintf(stderr, "do_write\n");
int res;
const uint32_t dest = 0xac010000;
@ -223,7 +233,7 @@ int do_write(struct ftdi_context * ftdi, const uint8_t * buf, const uint32_t siz
union serial_load::command_reply reply;
res = read_reply(ftdi, serial_load::reply::write, reply);
if (res != 0) {
return -1;
return -2;
}
fprintf(stderr, "remote: dest: %08x size: %08x\n", reply.arg[0], reply.arg[1]);
if (reply.arg[0] != dest || reply.arg[1] != size) {
@ -255,6 +265,7 @@ int do_write(struct ftdi_context * ftdi, const uint8_t * buf, const uint32_t siz
int do_jump(struct ftdi_context * ftdi)
{
fprintf(stderr, "do_jump\n");
int res;
const uint32_t dest = 0xac010000;
@ -266,7 +277,7 @@ int do_jump(struct ftdi_context * ftdi)
union serial_load::command_reply reply;
res = read_reply(ftdi, serial_load::reply::jump, reply);
if (res != 0) {
return -1;
return -2;
}
fprintf(stderr, "remote: jump: %08x\n", reply.arg[0]);
if (reply.arg[0] != dest || reply.arg[1] != 0) {
@ -276,47 +287,91 @@ int do_jump(struct ftdi_context * ftdi)
return 0;
}
int main(int argc, char * argv[])
int read_file(const char * filename, uint8_t ** buf, uint32_t * size)
{
if (argc < 2) {
fprintf(stderr, "argc\n");
return EXIT_FAILURE;
}
FILE * file = fopen(argv[1], "r");
FILE * file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, "fopen\n");
return EXIT_FAILURE;
return -1;
}
int ret;
ret = fseek(file, 0L, SEEK_END);
if (ret < 0) {
fprintf(stderr, "seek(SEEK_END)");
return EXIT_FAILURE;
fprintf(stderr, "fseek(SEEK_END)");
return -1;
}
long off = ftell(file);
ret = fseek(file, 0L, SEEK_SET);
if (ret < 0) {
fprintf(stderr, "seek(SEEK_SET)");
return EXIT_FAILURE;
fprintf(stderr, "fseek(SEEK_SET)");
return -1;
}
fprintf(stderr, "%s off %ld\n", argv[1], 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;
fprintf(stderr, "%s size %ld\n", filename, off);
*buf = (uint8_t *)malloc(off);
ssize_t fread_size = fread(*buf, 1, off, file);
if (fread_size < 0) {
fprintf(stderr, "fread");
return -1;
}
printf("%02x\n", buf[0]);
ret = fclose(file);
if (ret < 0) {
fprintf(stderr, "close");
fprintf(stderr, "fclose");
return -1;
}
*size = off;
return 0;
}
int do_read(struct ftdi_context * ftdi)
{
fprintf(stderr, "do_read\n");
int res;
const uint32_t src = 0xac010000;
const uint32_t size = 51584;
union serial_load::command_reply command = serial_load::read_command(src, 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::read, reply);
if (res != 0) {
return -2;
}
fprintf(stderr, "remote: src: %08x size: %08x\n", reply.arg[0], reply.arg[1]);
if (reply.arg[0] != src || reply.arg[1] != size) {
return -1;
}
uint32_t * buf = (uint32_t *)malloc(size);
res = ftdi_read_data(ftdi, (uint8_t *)buf, size);
assert(res >= 0);
assert((uint32_t)res == size);
uint32_t buf_crc = crc32((uint8_t*)buf, size);
union serial_load::command_reply crc_reply;
res = read_reply(ftdi, serial_load::reply::read_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 main(int argc, char * argv[])
{
if (argc < 2) {
fprintf(stderr, "argc\n");
return EXIT_FAILURE;
}
@ -336,15 +391,23 @@ int main(int argc, char * argv[])
int return_code = EXIT_SUCCESS;
uint8_t * buf;
uint32_t size;
res = read_file(argv[1], &buf, &size);
if (res < 0) {
return EXIT_FAILURE;
}
struct timespec start;
struct timespec end;
res = clock_gettime(CLOCK_MONOTONIC, &start);
assert(res >= 0);
int do_write_ret = do_write(ftdi, buf, off);
int do_write_ret = do_write(ftdi, buf, size);
res = clock_gettime(CLOCK_MONOTONIC, &end);
assert(res >= 0);
fprintf(stderr, "do_write time: %.03f\n", timespec_difference(&end, &start));
if (do_write_ret == 0) {
//do_read(ftdi);
do_jump(ftdi);
} else {
return_code = EXIT_FAILURE;