INL-retro-progdump/firmware/source/swim.c

396 lines
11 KiB
C

#include "swim.h"
//=================================================================================================
//
// SWIM operations
// This file includes all the swim functions possible to be called from the swim dictionary.
//
// See description of the commands contained here in shared/shared_dictionaries.h
//
//=================================================================================================
/* Desc:Function takes an opcode which was transmitted via USB
* then decodes it to call designated function.
* shared_dict_swim.h is used in both host and fw to ensure opcodes/names align
* Pre: Macros must be defined in firmware pinport.h
* opcode must be defined in shared_dict_swim.h
* Post:function call complete.
* Rtn: SUCCESS if opcode found, error if opcode not present or other problem.
*/
uint8_t swim_call( uint8_t opcode, uint8_t miscdata, uint16_t operand, uint8_t *rdata )
{
#define RD_LEN 0
#define RD0 1
#define RD1 2
#define BYTE_LEN 1
#define HWORD_LEN 2
switch (opcode) {
case SWIM_ACTIVATE: swim_activate(); break;
case SWIM_RESET: swim_reset(); break;
case SWIM_SRST:
rdata[RD_LEN] = BYTE_LEN;
rdata[RD0] = swim_out( 0x0000, 4); break;
case WOTF:
rdata[RD_LEN] = BYTE_LEN;
rdata[RD0] = swim_woft(operand, miscdata); break;
// case ROTF:
// rdata[RD_LEN] = BYTE_LEN;
// rdata[RD0] = swim_roft(); break;
default:
//opcode doesn't exist
return ERR_UNKN_SWIM_OPCODE;
}
return SUCCESS;
}
uint8_t swim_pin;
uint16_t swim_mask;
GPIO_TypeDef *swim_base;
void delay( uint16_t delay )
{
uint16_t i = 0;
for( i=0; i<delay; i++) {
NOP();
NOP();
} //16->11.8 on stmad
}
/* Desc:Initiate SWIM activate sequence
* Pre: swim_pin must be set and initialized via io.h
* Post:STM8 mcu SWIM active
* Rtn: SUCCESS if able to enter sucessfully.
*/
void swim_activate()
{
uint16_t i;
// pinport_call( CTL_OP_, 0, swim_pin, 0);
//toggles in this manner take 4.55-4.6usec on AVR
//toggles in this manner take 4.9-4.95usec on STM adapter
//pulse low for 16usec spec says 16usec
//but looking at working programmers they do twice the delays below
pinport_call( CTL_SET_LO_, 0, swim_pin, 0);
delay(16);
pinport_call( CTL_SET_HI_, 0, swim_pin, 0);
//toggle high->low T=1msec 4x
for( i = 0; i < 4; i++) {
//STM adapter 720 = 500.9usec
//TODO move this timed code into a timer to make timing more stable
//between boards, pinport.c code, etc....
#ifdef STM_INL6
delay(719);
#endif
#ifdef STM_ADAPTER
delay(720);
#endif
pinport_call( CTL_SET_LO_, 0, swim_pin, 0);
delay(718);
pinport_call( CTL_SET_HI_, 0, swim_pin, 0);
}
//toggle high->low T=0.5msec 4x
for( i = 0; i < 4; i++) {
//STM adapter 358 = 256usec
delay(356);
pinport_call( CTL_SET_LO_, 0, swim_pin, 0);
delay(355);
pinport_call( CTL_SET_HI_, 0, swim_pin, 0);
}
//pinport_call( CTL_IP_PU_, 0, swim_pin, 0);
//wait for device to take swim_pin low for ~16usec
//it's low for 128 SWIM clock sync pulse
//Best way to do this would be to wait for an interrupt
//on the swim pin going low, then have the isr count
//low time. If anything takes too long timeout.
//TODO
//return SUCCESS/FAIL depending on wether that ~16usec pulse was obtained
// return SUCCESS;
}
/* Desc:Hold swim pin low for >128 SWIM clocks (16usec)
* Pre: swim must be activated by
* Post:STM8 mcu SWIM comms are reset
* Rtn: SUCCESS if device responds with sync window.
*/
void swim_reset()
{
//pinport_call( CTL_OP_, 0, swim_pin, 0);
//pulse low for 16usec spec says 16usec
//but looking at working programmers they do very long resets
pinport_call( CTL_SET_LO_, 0, swim_pin, 0);
delay(16);
pinport_call( CTL_SET_HI_, 0, swim_pin, 0);
}
#define mov_swim_mask_r0() __asm ("mov r0, %[val]" : : [val] "r" (swim_mask) : "r0" )
#define mov_pushpull_r6() __asm ("mov r6, %[val]" : : [val] "r" (pushpull) : "r6" )
#define mov_opendrain_r7() __asm ("mov r7, %[val]" : : [val] "r" (opendrain) : "r7" )
//BSRR 0x18
#define str_r0_bset() __asm volatile ("strh r0, [%[mmio], #0x18]" : : [mmio] "r" (swim_base))
//BRR 0x28
#define str_r0_bres() __asm volatile ("strh r0, [%[mmio], #0x28]" : : [mmio] "r" (swim_base))
//OTYPER 0x04
#define pp_swim() __asm volatile ("strh r6, [%[mmio], #0x04]" : : [mmio] "r" (swim_base))
#define od_swim() __asm volatile ("strh r7, [%[mmio], #0x04]" : : [mmio] "r" (swim_base))
#define NO_RESP 0xFF
#define ACK 0x01
#define NAK 0x00
/* Desc:Transfer SWIM bit stream
* Always outputs '0' as first bit for header "from host"
* Will output len number of bits plus one for header
* Pre: swim_pin must be set and initialized via io.h
* stream has first data bit stored in bit15 - bit[15-len+2]
* pairity bit must be last bit in stream sequence bit[15-len+1]
* ie bit7 contains pairity bit for 8bit data stream
* Post:STM8 mcu SWIM active
* Rtn: 0xFF if no response, 0-NAK, 1-ACK.
*/
uint8_t swim_out(uint16_t stream, uint8_t len)//, GPIO_TypeDef *base)
{
//__asm("swim_out_begin:\n\t");
uint8_t return_val;
uint16_t pushpull = swim_base->OTYPER & ~swim_mask;
uint16_t opendrain = swim_base->OTYPER | swim_mask;
mov_swim_mask_r0(); //will store r0 to BSRR or BRR to set/clear
mov_pushpull_r6(); //will store r7 to OTYPER to drive high
mov_opendrain_r7(); //will store r6 to OTYPER to allow device to drive low
//store len in r5
__asm volatile ("mov r5, %[val]" : : [val] "r" (len) : "r5" );
//set flags so first bit is header '0' "from host"
//the stream comes in as 16bit value with stream bit 7 in bit position 15
//shift the stream left so the current transfer bit is in bit position 31
//15 -> 30 is 15bit shifts, this leaves header zero in bit position 31
//store stream in r4
__asm volatile ("mov r4, %[val]" : : [val] "r" (stream) : "r4" );
__asm volatile ("lsl r4, #15" : : : "r4", "cc" );
//NOTE! cortex M0 only supports 'S' versions of lsl, sub, and, etc type
//opcodes. I get compiler error for including the 's' at end of instruction
//but lsl is same as lsls on M0, so just use lsl to avoid compiler error...
// bit start:
__asm("bit_start:\n\t");
//always start going low
str_r0_bres();
//current bit is stored in bit31 and Negative flag is set if
//current bit is '1'
__asm volatile ("bpl cur_bit_zero\n\t");
//go high since current bit is '1'
pp_swim();
str_r0_bset();
od_swim();
__asm volatile ("b det_next_bit\n\t");
__asm("cur_bit_zero:\n\t");
//must delay same amount of time as instructions above since branch
//add delay here until '1' and '0' are same length
NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
NOP();
__asm("det_next_bit:\n\t");
//determine if this is the last bit
__asm volatile ("sub r5, #0x01" : : : "r5", "cc" );
//if last bit, go to stream end to prepare for ACK/NAK latch
__asm volatile ("bmi stream_end\n\t");
//determine next bit value
__asm volatile ("lsl r4, #1" : : : "r4", "cc" );
//Negative flag is now set for '1', and clear for '0'
//delay until 'go high' time for '0'
//add delay here to make all bit transfers longer
NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP(); NOP();
//always go high for '0' (no effect if already high for '1')
pp_swim();
str_r0_bset();
od_swim();
//go to bit start
__asm volatile ("b bit_start\n\t");
//stream end:
__asm("stream_end:\n\t");
//delay until 'go high' time for '0'
NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP();
//always go high for '0' (no effect if already high for '1')
pp_swim();
str_r0_bset();
od_swim();
//delay until time to latch ACK/NAK from device
NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
//8x NOP's misread at times...
//6x NOP's always misread, so must be too early
NOP(); //9x NOP's and we're getting stable reads
//first need to ensure device is actually responding
//sample when output should be low for a 1 or 0
//str_r0_bres(); //debug set low to denote when swim pin is being sampled with logic anaylzer
//sampling ~100nsec into bit transfer (low for 125nsec min)
//latch SWIM pin value from IDR 0x10
__asm volatile ("ldrh r3, [%[mmio], #0x10]" : : [mmio] "r" (swim_base) : "r3");
__asm volatile ("and r3, r0" : : : "r3", "cc" );
//if it wasn't low, then the device didn't respond, so return error designating that
//__asm volatile ("bne no_response\n\t");
__asm ("mov %[rv], r3" : [rv] "=r" (return_val) );
if (return_val != 0) {
return NO_RESP;
}
//now delay until ~half way through bit transfer to sense 1-ACK or 0-NAK
NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
//sampling 1.35usec into bit transfer (~half way)
NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
//sampling 2.00usec into bit transfer oscope shows 2.7volts on stmad w/open drain
//debug set low to denote when swim pin is being sampled with logic anaylzer
//str_r0_bres();
//pp_swim();
//latch SWIM pin value from IDR 0x10
__asm volatile ("ldr r3, [%[mmio], #0x10]" : : [mmio] "r" (swim_base) : "r3");
__asm volatile ("and r3, r0" : : : "r3", "cc" );
//move r3 to return val reg
__asm volatile ("mov %[rv], r3" : [rv] "=r" (return_val) );
//mask out swim pin so return value is 0 or non-zero based on device output
//return_val &= swim_mask;
if (return_val == 0x00) {
return NAK;
}
//made it to here then we got an ACK
return ACK;
}
/* Function to get parity of number n. It returns 0
* if n has odd parity, and returns 0xFF if n has even
* parity */
uint16_t append_pairity(uint8_t n)
{
//shift incoming data to upper byte
uint16_t data_pb = (n<<8);
uint8_t parity = 0;
while (n) {
parity = ~parity;
n = n & (n - 1);
}
if ( parity ) {
return (data_pb | 0x80);
} else {
return data_pb;
}
}
/* Desc:write byte to SWIM
* Pre: swim must be activated
* Post:
* Rtn: 0-NAK, 1-ACK beware, no response looks like ACK!
*/
uint8_t swim_woft(uint16_t addr, uint8_t data)
{
uint16_t data_pb;
uint8_t ack;
uint8_t len = 0;
//bit sequence:
//1bit header "0" Host comm
//3bit command b2-1-0 "010" WOTF
//1bit pairity xor of cmd "1"
//1bit ACK "1" or NAK "0" from device
// 0b0_0101
data_pb = 0x5000;
// data_pb = 0x4000; //test wrong pairity
len = 4; //data + pairity ( '0' header not included)
ack = swim_out( data_pb, len);
if (ack == NO_RESP) {
return NO_RESP;
} else if (ack == NAK) {
return NAK;
//wait and resend WOFT command
}
//write N "number of bytes for WOTF"
data_pb = 0x0180;
len = 9;
ack = swim_out( data_pb, len);
if (ack == NO_RESP) return NO_RESP;
//write @E extended address of write
//always 0x00 since targetting stm8s003 which only has one section
data_pb = 0x0000;
len = 9;
ack = swim_out( data_pb, len);
if (ack == NO_RESP) return NO_RESP;
//write @H high address of write
data_pb = append_pairity( addr>>8 );
len = 9;
ack = swim_out( data_pb, len);
if (ack == NO_RESP) return NO_RESP;
//write @L high address of write
data_pb = append_pairity( addr );
len = 9;
ack = swim_out( data_pb, len);
if (ack == NO_RESP) return NO_RESP;
//write DATA portion of write
data_pb = append_pairity( data );
len = 9;
ack = swim_out( data_pb, len);
//More bytes can be written
//any time NAK is recieved must resend byte
return ack;
}