8000 Calculate CRC using SPI peripheral · Issue #75 · cnlohr/rv003usb · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Calculate CRC using SPI peripheral #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sw opened this issue Dec 31, 2024 · 2 comments
Open

Calculate CRC using SPI peripheral #75

sw opened this issue Dec 31, 2024 · 2 comments

Comments

@sw
Copy link
sw commented Dec 31, 2024

Here's an idea I came up with, but I fear it's not really practical. The SPI master can calculate a 16-bit CRC with an arbitrary polynomial. Unfortunately there's quite some bit wrangling necessary:

  • need to first feed in 0x57 0x15 to get the starting value from 0000 to FFFF
  • operates on 2 bytes at a time
  • output is both inverted and reversed :-(
#include "ch32v003fun.h"
#include <stdio.h>

uint8_t __attribute__( ( aligned( 2 ) ) ) buf[] = { 0x57, 0x15, '1', '2', '3', '4', '5', '6', '7', '8' };

int main()
{
	SystemInit();

	RCC->APB2PRSTR = RCC_SPI1RST;
	RCC->APB2PRSTR = 0;
	RCC->APB2PCENR |= RCC_APB2Periph_SPI1;
	RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;

	SPI1->CTLR1 = 0x0080 | SPI_NSS_Soft | SPI_DataSize_16b | SPI_Mode_Master | SPI_Direction_2Lines_FullDuplex |
	              SPI_BaudRatePrescaler_2;
	SPI1->CTLR2 = SPI_CTLR2_TXDMAEN;
	SPI1->CRCR = 0x8005;
	SPI1->CTLR1 |= SPI_CTLR1_CRCEN;

	SPI1->CTLR1 |= CTLR1_SPE_Set;

	DMA1_Channel3->PADDR = (uint32_t)&SPI1->DATAR;
	DMA1_Channel3->MADDR = (uint32_t)buf;
	DMA1_Channel3->CFGR = DMA_M2M_Disable | DMA_Priority_VeryHigh | DMA_MemoryDataSize_HalfWord |
	                      DMA_PeripheralDataSize_HalfWord | DMA_MemoryInc_Enable | DMA_Mode_Normal |
	                      DMA_DIR_PeripheralDST;

	DMA1_Channel3->CFGR &= ~DMA_CFGR1_EN;
	DMA1_Channel3->CNTR = sizeof( buf ) / 2;

	uint32_t timestamp = SysTick->CNT;
	DMA1_Channel3->CFGR |= DMA_CFGR1_EN;

	while ( !( DMA1->INTFR & DMA1_IT_TC3 ) );
	while ( SPI1->STATR & SPI_STATR_BSY );

	uint32_t v = SPI1->TCRCR ^ 0xffff;

	v = ( ( v >> 1 ) & 0x5555 ) | ( ( v & 0x5555 ) << 1 );
	v = ( ( v >> 2 ) & 0x3333 ) | ( ( v & 0x3333 ) << 2 );
	v = ( ( v >> 4 ) & 0x0F0F ) | ( ( v & 0x0F0F ) << 4 );
	v = ( ( v >> 8 ) & 0x00FF ) | ( ( v & 0x00FF ) << 8 );

	timestamp = SysTick->CNT - timestamp;

	printf( "crc=%04lx ticks=%lu\n", v, timestamp );
	while (1);
}

Output:

crc=c822 ticks=32

Check: https://www.crccalc.com/?crc=12345678&method=CRC-16/USB&datatype=ascii&outtype=hex

Maybe someone can find a way to do this more efficiently...

@sw
Copy link
Author
sw commented Dec 31, 2024

Looking at the assembly code some more...

	li t0, 0x0000
	c.bnez a2, done_poly_check
	li t0, 0xa001
	li a2, 0xffff
done_poly_check:
...
	bnez t0, done_poly_check2
	li a2, 0xffff
done_poly_check2:

a2 always ends up equal to 0xffff, so why is this check done twice?

It might be cleaner to pass this to usb_send_data, instead of poly_function:

typedef enum
{
  CRC_EXCLUDE = 0,
  CRC_INCLUDE = 0xa001,
} crc_t;

@cnlohr
Copy link
Owner
cnlohr commented Jan 1, 2025

It would be a fair bit of work to rework everything. I am not sure how important it is to do, either, since we calculate the CRC while we're receiving the frame. There isn't much else we can be doing during that time. But, maybe if someone ever makes a USB full-speed implementation that would be useful?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants
0