#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 uint16_t *ret_hword = (uint16_t*) &rdata[1]; #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] = HWORD_LEN; //this assignment actually undoes the byte swap //first index of data includes NAK/ACK just like write routines which only return ACK/NAK //second index of data includes actual byte read back *ret_hword = swim_roft( operand ); 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; i11.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:read byte from SWIM * Pre: swim must be activated * Post: * Rtn: should return success/error and value read */ //must match swim.s .equ statements!!! #define SWIM_RD_LS 0x01 #define SWIM_WR_LS 0x02 #define SWIM_RD_HS 0x11 #define SWIM_WR_HS 0x12 uint16_t swim_roft(uint16_t addr) { //If >24Mhz SYSCLK, must add wait state to flash //can also enable prefetch buffer FLASH->ACR = FLASH_ACR_PRFTBE | 0x0001; //switch to 48Mhz RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_PLL; uint16_t data_pb; uint32_t ack_data; uint32_t spddir_len; //bit sequence: //1bit header "0" Host comm //3bit command b2-1-0 "001" ROTF //1bit pairity xor of cmd "1" //1bit ACK "1" or NAK "0" from device // 0b0_0011 data_pb = 0x3000; spddir_len = (SWIM_WR_LS<<16) | 4; //data + pairity ( '0' header not included) ack_data = swim_xfr( data_pb, spddir_len, swim_base, swim_mask); if (ack_data != ACK) goto end_swim; //write N "number of bytes for ROTF" data_pb = 0x0180; spddir_len = (SWIM_WR_LS<<16) | 9; ack_data = swim_xfr( data_pb, spddir_len, swim_base, swim_mask); if (ack_data != ACK) goto end_swim; //write @E extended address of write //always 0x00 since targetting stm8s003 which only has one section data_pb = 0x0000; spddir_len = (SWIM_WR_LS<<16) | 9; ack_data = swim_xfr( data_pb, spddir_len, swim_base, swim_mask); if (ack_data != ACK) goto end_swim; //write @H high address of write data_pb = append_pairity( addr>>8 ); spddir_len = (SWIM_WR_LS<<16) | 9; ack_data = swim_xfr( data_pb, spddir_len, swim_base, swim_mask); if (ack_data != ACK) goto end_swim; //write @L high address of write data_pb = append_pairity( addr ); //this is a read xfr because device will output data immediately after //writting last byte of command info spddir_len = (SWIM_RD_LS<<16) | 9; ack_data = swim_xfr( data_pb, spddir_len, swim_base, swim_mask); //read DATA portion of write //More bytes can be written //any time NAK is recieved must resend byte end_swim: RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_HSE; //Don't need wait states or prefetch buffer anymore FLASH->ACR = 0x0000; return ack_data; } /* Desc:write byte to SWIM * Pre: swim must be activated * Post: * Rtn: 0-NAK, 1-ACK, 0xFF no response */ 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; }