/* Small example showing how to use the SWIO programming pin to 
   do printf through the debug interface */

#define SYSTEM_CORE_CLOCK 24000000
#include "ch32v003fun.h"
#include <stdio.h>

uint32_t count;


// This is a complicated way to do it from C land, as a demonstration

// Tell the compiler to put this code in the .data section.  That
// will cause the startup code to copy it from flash into RAM where
// it can be easily modified at runtime.
uint32_t ReadCSRSelfModify( uint16_t whichcsr ) __attribute__(( section(".data"))) __attribute__((noinline));
uint32_t ReadCSRSelfModify( uint16_t whichcsr )
{
	uint32_t ret;

	// Tricky: GCC will make this variable "point to" the opcode
	// of the csrr instruction below.
	volatile extern uint32_t readCSRLabel;

	// We have to put this here to "force" the compiler to order the
	// instructions in this way.  Otherwise, the compiler will try
	// to optimize the code and inline the assembly into something where
	// our global handle into assembly code becomes meaningless.
	// Annoyingly, it has to contain at least one instruction :(
	asm volatile( "nop" );

	// 000026f3 is csrrs a3, 0x000, x0; So, we modify it, placing the
	// CSR we want to read in the top 12 bits of the instruction.

	readCSRLabel = 0x000026f3 | (whichcsr << 20);

	// The actual assembly block inserted into the C function.  This
	// defines the local label, globally, so the linker will be able to
	// pick it up.  We also need to used a fixed register, a3, so we
	// can know what opcode we want to use, then we can let C tell us
	// what register it would like the value in.
	//
	// The fence is needed to make sure the CPU knows to not use
	// cached instructions.
	//
	// The constraints are "ret" is a "write" register, and register a3
	// is going to be clobbered by the assembly code.
	asm volatile( 
		".global readCSRLabel   \n"
		"	fence               \n"
		"readCSRLabel:          \n"
		"	csrrs a3, 0x000, x0 \n"
		"	addi %[ret], a3, 0  \n"
		 : [ret]"=r"(ret) : : "a3" );

	return ret;
}


uint32_t ReadCSRSelfModifySimple( uint16_t whichcsr ) __attribute__(( section(".data"))) __attribute__((noinline));
uint32_t ReadCSRSelfModifySimple( uint16_t whichcsr )
{
	uint32_t ret;
	uint32_t csrcmd = 0x000026f3 | ( whichcsr << 20);
	asm volatile( 
		".global readCSRLabel   \n"
		"   la a3, readCSRLabel \n"
		"   sw %[csrcmd], 0(a3) \n"
		"   fence               \n"
		"readCSRLabel:          \n"
		"	csrrs a3, 0x000, x0 \n"
		"	addi %[ret], a3, 0  \n"
		 : [ret]"=r"(ret) : [csrcmd]"r"(csrcmd) : "a3" );

	return ret;
}


int main()
{
	SystemInit48HSI();
	SetupDebugPrintf();

	WaitForDebuggerToAttach();

	// Enable GPIOs
	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC;
	puts( "Print all non-zero CSRs:" );
	int i;
	for( i = 0x000; i < 0x1000; i++ )
	{
		uint32_t rv =  ReadCSRSelfModifySimple( i );
		if( rv )
			printf( "%03x = %08lx\n", i, rv );
	}
	printf( "Done\n" );
	for(;;);
}