#include "firmware/固件/drill_target/src/drill_spi_transport.c"
#include "firmware/固件/drill_target/src/drill_pwm_stage.c"
#include <assert.h>
#include <stdio.h>
#include <string.h>
RCC_TypeDef rcc;GPIO_TypeDef ga,gb,gf;TIM_TypeDef timer;SPI_TypeDef spi;
static unsigned cs_changes;
static void pins(GPIO_TypeDef *p){p->ODR=(p->ODR|(p->BSRR&65535u))&~(p->BSRR>>16);p->IDR=p->ODR;p->BSRR=0;}
void host_dsb(void){if(gb.BSRR&(1u<<12|1u<<28))++cs_changes;pins(&ga);pins(&gb);pins(&gf);if(rcc.APB1RSTR1&RCC_APB1RSTR1_SPI2RST)memset(&spi,0,sizeof spi);}
void host_wait(void){assert(0);}
static void fixture(uint32_t now){
 memset(&rcc,0,sizeof rcc);memset(&ga,0,sizeof ga);memset(&gb,0,sizeof gb);memset(&gf,0,sizeof gf);memset(&timer,0,sizeof timer);memset(&spi,0,sizeof spi);state=UNINITIALIZED;cs_changes=0;
 rcc.CR=RCC_CR_HSION|RCC_CR_HSIRDY;rcc.CFGR=RCC_CFGR_SW_HSI|RCC_CFGR_SWS_HSI;rcc.AHB2ENR=RCC_AHB2ENR_GPIOAEN|RCC_AHB2ENR_GPIOBEN|RCC_AHB2ENR_GPIOFEN;rcc.APB2ENR=RCC_APB2ENR_TIM1EN;
 for(unsigned p=5;p<=10;++p)if(p!=6)ga.MODER|=1u<<(p*2);gb.MODER=5;gf.MODER=4;
 assert(drill_spi_init(now));assert(drill_spi_available());gf.ODR=gf.IDR=2;spi.SR=SPI_SR_TXE;
}
static void off(void){assert(drill_spi_failed());assert(!(ga.ODR&((1u<<5)|(1u<<7)|(7u<<8)))&&!(gb.ODR&3)&&!(gf.ODR&2));assert(gb.ODR&(1u<<12));assert(!(spi.CR1&SPI_CR1_SPE));assert(timer.CCER==0&&!(timer.BDTR&TIM_BDTR_MOE)&&!(timer.CR1&TIM_CR1_CEN));uint16_t v=0xffff;assert(!drill_spi_get(&v,last)&&v==0xffff);}
static void complete(uint16_t frame,uint32_t start){
 assert(drill_spi_start(frame,start));assert(!(gb.ODR&(1u<<12)));unsigned changes=cs_changes;
 drill_spi_poll(start);assert(state==SETUP);
 drill_spi_poll(start+1);assert(state==SETUP);
 drill_spi_poll(start+2);assert(state==RECEIVE&&(uint16_t)spi.DR==frame);
 spi.SR=SPI_SR_BSY;
 for(unsigned step=10;step<=30;step+=10){drill_spi_poll(start+step);assert(state==RECEIVE);}
 spi.DR=frame^0xaa55;spi.SR=SPI_SR_BSY|SPI_SR_RXNE;drill_spi_poll(start+34);assert(state==DRAIN);
 spi.SR=SPI_SR_BSY;drill_spi_poll(start+35);assert(state==DRAIN);
 spi.SR=SPI_SR_TXE;drill_spi_poll(start+36);assert(state==HOLD);assert(cs_changes==changes);
 drill_spi_poll(start+37);assert(state==HOLD);
 drill_spi_poll(start+38);assert(state==DONE&&cs_changes==changes+1);
 uint16_t out=0;assert(drill_spi_active());assert(drill_spi_get(&out,start+38)&&out==(uint16_t)(frame^0xaa55));
 assert(!drill_spi_active());uint16_t stale=0x1234;assert(!drill_spi_get(&stale,start+38)&&stale==0x1234);
 assert(!drill_spi_start(frame,start+39));assert(drill_spi_start(frame,start+40));
}
int main(void){
 unsigned cases=0;
 for(unsigned frame=0;frame<65536;++frame){fixture(UINT32_MAX);unsigned parity=0;for(unsigned bit=0;bit<16;++bit)parity^=(frame>>bit)&1;
  if(parity){SPI_TypeDef before=spi;unsigned changes=cs_changes;assert(!drill_spi_start(frame,1));assert(cs_changes==changes&&memcmp(&spi,&before,sizeof spi)==0);}
  else complete(frame,1);++cases;
 }
 for(unsigned reg=0;reg<2;++reg)for(unsigned bit=0;bit<32;++bit){fixture(UINT32_MAX);assert(drill_spi_start(0,1));volatile uint32_t *p=reg?&spi.CR2:&spi.CR1;*p^=1u<<bit;drill_spi_poll(2);off();++cases;}
 uint32_t errors[]={SPI_SR_MODF,SPI_SR_OVR,SPI_SR_CRCERR,SPI_SR_FRE};
 for(unsigned phase=0;phase<4;++phase)for(unsigned e=0;e<4;++e){fixture(UINT32_MAX);assert(drill_spi_start(0,1));if(phase>=1)drill_spi_poll(3);if(phase>=2){spi.SR=SPI_SR_RXNE|SPI_SR_BSY;drill_spi_poll(4);spi.SR=SPI_SR_TXE;}if(phase>=3)drill_spi_poll(5);spi.SR|=errors[e];drill_spi_poll(6);off();++cases;}
 for(unsigned n=0;n<10;++n){fixture(UINT32_MAX);assert(drill_spi_start(0,1));
 if(n==0)rcc.CR&=~RCC_CR_HSIRDY;if(n==1)rcc.CFGR|=RCC_CFGR_PPRE1_DIV2;
 if(n==2)ga.ODR=ga.IDR=1u<<5;if(n==3)gf.ODR=gf.IDR=0;
 if(n==4)timer.BDTR|=TIM_BDTR_MOE;if(n==5)timer.CCER=1;if(n==6)timer.CR1|=TIM_CR1_CEN;
 if(n==7)gb.AFR[1]^=1u<<20;if(n==8)gb.PUPDR|=1u<<26;if(n==9)gb.MODER^=1u<<26;
 drill_spi_poll(2);off();++cases;}
 fixture(UINT32_MAX);assert(drill_spi_start(0,1));drill_spi_poll(12);off();++cases;
 fixture(UINT32_MAX);assert(drill_spi_start(0,1));drill_spi_poll(0);off();++cases;
 fixture(UINT32_MAX-3);complete(0,UINT32_MAX-1);++cases;
 fixture(UINT32_MAX);gf.ODR=gf.IDR=0;assert(!drill_spi_start(0,1));assert(state==IDLE);++cases;
 fixture(UINT32_MAX);spi.SR|=SPI_SR_RXNE;assert(!drill_spi_start(0,1));off();++cases;
 fixture(UINT32_MAX);assert(drill_spi_start(0,1));assert(!drill_spi_start(0,2));++cases;
 printf("%u SPI TRANSPORT CASES PASS\n",cases);
}
