rework structure

This commit is contained in:
2023-08-20 15:00:51 +02:00
parent 4c2208c1ac
commit 314ceeb072
381 changed files with 10697 additions and 14653 deletions

View File

@ -0,0 +1,22 @@
/*
* bsp.h
*
* Created on: 30.07.2018
* Author: eyck
*/
#ifndef BSP_H_
#define BSP_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <fe300prci/fe300prci_driver.h>
#include <platform.h>
#include <encoding.h>
extern void trap_entry();
#ifdef __cplusplus
}
#endif
#endif /* BSP_H_ */

View File

@ -0,0 +1,123 @@
/*
* delay.c
*
* Created on: 30.07.2018
* Author: eyck
*/
#include "delay.h"
#define rdmcycle(x) { \
uint32_t lo, hi, hi2; \
__asm__ __volatile__ ("1:\n\t" \
"csrr %0, mcycleh\n\t" \
"csrr %1, mcycle\n\t" \
"csrr %2, mcycleh\n\t" \
"bne %0, %2, 1b\n\t" \
: "=r" (hi), "=r" (lo), "=r" (hi2)) ; \
*(x) = lo | ((uint64_t) hi << 32); \
}
typedef struct {
uint32_t n;
uint32_t mult;
uint32_t shift;
} int_inverse ;
int_inverse f_cpu_1000_inv;
int_inverse f_cpu_1000000_inv;
uint32_t F_CPU=1000000;
void calc_inv(uint32_t n, int_inverse * res){
uint32_t one = ~0;
uint32_t d = one/n;
uint32_t r = one%n + 1;
if (r >= n) ++d;
if (d == 0) --d;
uint32_t shift = 0;
while ((d & 0x80000000) == 0){
d <<= 1;
++shift;
}
res->n = n;
res->mult = d;
res->shift = shift;
}
uint32_t divide32_using_inverse(uint32_t n, int_inverse *inv){
uint32_t d = (uint32_t)(((uint64_t)n * inv->mult) >> 32);
d >>= inv->shift;
if (n - d*inv->n >= inv->n) ++d;
return d;
}
// Almost full-range 64/32 divide.
// If divisor-1 has i bits, then the answer is exact for n of up to 64-i bits
// e.g. for divisors up to a million, n can have up to 45 bits
// On RV32IM with divide32_using_inverse inlines this uses 5 multiplies,
// 33 instructions, zero branches, 3 loads, 0 stores.
uint64_t divide64_using_inverse(uint64_t n, int_inverse *inv){
uint32_t preshift = (31 - inv->shift) & 31;
uint64_t d = (uint64_t)divide32_using_inverse(n >> preshift, inv) << preshift;
uint32_t r = n - d * inv->n;
d += divide32_using_inverse(r, inv);
return d;
}
uint32_t millis(){
uint64_t x;
rdmcycle(&x);
x = divide64_using_inverse(x, &f_cpu_1000_inv);
return((uint32_t) (x & 0xFFFFFFFF));
}
uint32_t micros(void){
uint64_t x;
rdmcycle(&x);
// For Power-of-two MHz F_CPU,
// this compiles into a simple shift,
// and is faster than the general solution.
#if F_CPU==16000000
x = x / (F_CPU / 1000000);
#else
#if F_CPU==256000000
x = x / (F_CPU / 1000000);
#else
x = divide64_using_inverse(x, &f_cpu_1000000_inv);
#endif
#endif
return((uint32_t) (x & 0xFFFFFFFF));
}
void delayMS(uint32_t dwMs){
uint64_t current, later;
rdmcycle(&current);
later = current + dwMs * (F_CPU/1000);
if (later > current){ // usual case
while (later > current)
rdmcycle(&current);
} else { // wrap. Though this is unlikely to be hit w/ 64-bit mcycle
while (later < current)
rdmcycle(&current);
while (current < later)
rdmcycle(&current);
}
}
void delayUS(uint32_t dwUs){
uint64_t current, later;
rdmcycle(&current);
later = current + dwUs * (F_CPU/1000000);
if (later > current){ // usual case
while (later > current)
rdmcycle(&current);
} else {// wrap. Though this is unlikely to be hit w/ 64-bit mcycle
while (later < current)
rdmcycle(&current);
while (current < later)
rdmcycle(&current);
}
}

View File

@ -0,0 +1,25 @@
/*
* delay.h
*
* Created on: 30.07.2018
* Author: eyck
*/
#ifndef DELAY_H_
#define DELAY_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t F_CPU;
void delayMS(uint32_t dwMs);
void delayUS(uint32_t dwUs);
#ifdef __cplusplus
}
#endif
#endif /* DELAY_H_ */

View File

@ -0,0 +1,16 @@
/*
* peripherals.c
*
* Created on: 10.09.2018
* Author: eyck
*/
#include "hifive1_io.h"
template<> volatile bool qspi0::spi_active=false;
template<> volatile bool qspi1::spi_active=false;
template<> volatile bool qspi2::spi_active=false;
template<> volatile bool pwm0::pwm_active=false;
template<> volatile bool pwm1::pwm_active=false;
template<> volatile bool pwm2::pwm_active=false;

View File

@ -0,0 +1,26 @@
/*
* peripherals.h
*
* Created on: 29.07.2018
* Author: eyck
*/
#ifndef HIFIVE1_IO_H_
#define HIFIVE1_IO_H_
#include "io/gpio.h"
#include "io/spi.h"
#include "io/pwm.h"
#include "io/uart.h"
using gpio0=gpio_regs<0x10012000>;
using uart0=uart_regs<0x10013000>;
using uart1=uart_regs<0x10023000>;
using qspi0=spi_regs<0x10014000>;
using qspi1=spi_regs<0x10024000>;
using qspi2=spi_regs<0x10034000>;
using pwm0 =pwm_regs<0x10015000>;
using pwm1 =pwm_regs<0x10025000>;
using pwm2 =pwm_regs<0x10035000>;
#endif /* HIFIVE1_IO_H_ */

View File

@ -0,0 +1,89 @@
/*
* gpio.h
*
* Created on: 29.07.2018
* Author: eyck
*/
#ifndef GPIO_H_
#define GPIO_H_
#include <sifive/devices/gpio.h>
#include <cstdint>
template<uint32_t BASE_ADDR>
class gpio_regs {
public:
static inline uint32_t& value_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_INPUT_VAL);
}
static inline uint32_t& input_en_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_INPUT_EN);
}
static inline uint32_t& output_en_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_OUTPUT_EN);
}
static inline uint32_t& port_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_OUTPUT_VAL);
}
static inline uint32_t& pue_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_PULLUP_EN);
}
static inline uint32_t& ds_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_DRIVE);
}
static inline uint32_t& rise_ie_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_RISE_IE);
}
static inline uint32_t& rise_ip_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_RISE_IP);
}
static inline uint32_t& fall_ie_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_FALL_IE);
}
static inline uint32_t& fall_ip_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_FALL_IP);
}
static inline uint32_t& high_ie_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_HIGH_IE);
}
static inline uint32_t& high_ip_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_HIGH_IP);
}
static inline uint32_t& low_ie_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_LOW_IE);
}
static inline uint32_t& low_ip_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_LOW_IP);
}
static inline uint32_t& iof_en_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_IOF_EN);
}
static inline uint32_t& iof_sel_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_IOF_SEL);
}
static inline uint32_t& out_xor_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+GPIO_OUTPUT_XOR);
}
};
#endif /* GPIO_H_ */

View File

@ -0,0 +1,122 @@
/*
* pwm.h
*
* Created on: 29.07.2018
* Author: eyck
*/
#ifndef PWM_H_
#define PWM_H_
#include <sifive/devices/pwm.h>
#include "util/bit_field.h"
#include <limits>
#include <cstdint>
template<uint32_t BASE_ADDR>
class pwm_regs {
public:
BEGIN_BF_DECL(pwmcfg_t, uint32_t);
BF_FIELD(scale, 0, 4);
BF_FIELD(sticky, 8, 1);
BF_FIELD(zerocmp, 9, 1);
BF_FIELD(deglitch, 10, 1);
BF_FIELD(enalways, 12, 1);
BF_FIELD(enoneshot, 13, 1);
BF_FIELD(cmp0center, 16, 1);
BF_FIELD(cmp1center, 17, 1);
BF_FIELD(cmp2center, 18, 1);
BF_FIELD(cmp3center, 19, 1);
BF_FIELD(cmp0gang, 24, 1);
BF_FIELD(cmp1gang, 25, 1);
BF_FIELD(cmp2gang, 26, 1);
BF_FIELD(cmp3gang, 27, 1);
BF_FIELD(cmp0ip, 28, 1);
BF_FIELD(cmp1ip, 29, 1);
BF_FIELD(cmp2ip, 30, 1);
BF_FIELD(cmp3ip, 31, 1);
END_BF_DECL();
BEGIN_BF_DECL(pwms_t, uint32_t);
BF_FIELD(s, 0, 16);
END_BF_DECL() r_pwms;
BEGIN_BF_DECL(pwmcmp0_t, uint32_t);
BF_FIELD(cmp0, 0, 16);
END_BF_DECL() r_pwmcmp0;
BEGIN_BF_DECL(pwmcmp1_t, uint32_t);
BF_FIELD(cmp0, 0, 16);
END_BF_DECL() r_pwmcmp1;
BEGIN_BF_DECL(pwmcmp2_t, uint32_t);
BF_FIELD(cmp0, 0, 16);
END_BF_DECL() r_pwmcmp2;
BEGIN_BF_DECL(pwmcmp3_t, uint32_t);
BF_FIELD(cmp0, 0, 16);
END_BF_DECL() r_pwmcmp3;
static inline pwmcfg_t& cfg_reg(){
return *reinterpret_cast<pwmcfg_t*>(BASE_ADDR+PWM_CFG);
}
static inline uint32_t& count_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+PWM_COUNT);
}
static inline pwms_t& s_reg(){
return *reinterpret_cast<pwms_t*>(BASE_ADDR+PWM_S);
}
static inline pwmcmp0_t& cmp0_reg(){
return *reinterpret_cast<pwmcmp0_t*>(BASE_ADDR+PWM_CMP0);
}
static inline pwmcmp1_t& cmp1_reg(){
return *reinterpret_cast<pwmcmp1_t*>(BASE_ADDR+PWM_CMP1);
}
static inline pwmcmp2_t& cmp2_reg(){
return *reinterpret_cast<pwmcmp2_t*>(BASE_ADDR+PWM_CMP2);
}
static inline pwmcmp3_t& cmp3_reg(){
return *reinterpret_cast<pwmcmp3_t*>(BASE_ADDR+PWM_CMP3);
}
static inline bool oneshot_delay(long delay_us){
auto scaling_factor=0;
while(delay_us/(1<<scaling_factor) > std::numeric_limits<unsigned short>::max()){
scaling_factor++;
}
cfg_reg()=0;
count_reg()=0;
cfg_reg().scale=4+scaling_factor; // divide by 16 so we get 1us per pwm clock
cmp0_reg().cmp0 = delay_us/(1<<scaling_factor);
pwm_active=true;
cfg_reg().enoneshot=true;
do{
asm("wfi");
asm("nop");
}while(pwm_active);
return true;
}
static void pwm_interrupt_handler(){
cfg_reg().cmp0ip=false;
pwm_active=false;
}
inline
static bool is_active(){ return pwm_active; }
inline
static void set_active() {pwm_active=true;}
private:
static volatile bool pwm_active;
};
#endif /* PWM_H_ */

View File

@ -0,0 +1,200 @@
/*
* spi.h
*
* Created on: 29.07.2018
* Author: eyck
*/
#ifndef SPI_H_
#define SPI_H_
#include <sifive/devices/spi.h>
#include "util/bit_field.h"
#include <array>
#include <cstdint>
template<uint32_t BASE_ADDR>
class spi_regs {
public:
// storage declarations
BEGIN_BF_DECL(sckdiv_t, uint32_t);
BF_FIELD(div, 0, 12);
END_BF_DECL();
BEGIN_BF_DECL(sckmode_t, uint32_t);
BF_FIELD(pha, 0, 1);
BF_FIELD(pol, 1, 1);
END_BF_DECL();
uint32_t r_csid;
uint32_t r_csdef;
BEGIN_BF_DECL(csmode_t, uint32_t);
BF_FIELD(mode, 0, 2);
END_BF_DECL();
BEGIN_BF_DECL(delay0_t, uint32_t);
BF_FIELD(cssck, 0, 8);
BF_FIELD(sckcs, 16, 8);
END_BF_DECL();
BEGIN_BF_DECL(delay1_t, uint32_t);
BF_FIELD(intercs, 0, 16);
BF_FIELD(interxfr, 16, 8);
END_BF_DECL();
BEGIN_BF_DECL(fmt_t, uint32_t);
BF_FIELD(proto, 0, 2);
BF_FIELD(endian, 2, 1);
BF_FIELD(dir, 3, 1);
BF_FIELD(len, 16, 4);
END_BF_DECL();
BEGIN_BF_DECL(txdata_t, uint32_t);
BF_FIELD(data, 0, 8);
BF_FIELD(full, 31, 1);
END_BF_DECL() r_txdata;
BEGIN_BF_DECL(rxdata_t, uint32_t);
BF_FIELD(data, 0, 8);
BF_FIELD(empty, 31, 1);
END_BF_DECL();
BEGIN_BF_DECL(txmark_t, uint32_t);
BF_FIELD(txmark, 0, 3);
END_BF_DECL();
BEGIN_BF_DECL(rxmark_t, uint32_t);
BF_FIELD(rxmark, 0, 3);
END_BF_DECL();
BEGIN_BF_DECL(fctrl_t, uint32_t);
BF_FIELD(en, 0, 1);
END_BF_DECL();
BEGIN_BF_DECL(ffmt_t, uint32_t);
BF_FIELD(cmd_en, 0, 1);
BF_FIELD(addr_len, 1, 2);
BF_FIELD(pad_cnt, 3, 4);
BF_FIELD(cmd_proto, 7, 2);
BF_FIELD(addr_proto, 9, 2);
BF_FIELD(data_proto, 11, 2);
BF_FIELD(cmd_code, 16, 8);
BF_FIELD(pad_code, 24, 8);
END_BF_DECL();
BEGIN_BF_DECL(ie_t, uint32_t);
BF_FIELD(txwm, 0, 1);
BF_FIELD(rxwm, 1, 1);
END_BF_DECL();
BEGIN_BF_DECL(ip_t, uint32_t);
BF_FIELD(txwm, 0, 1);
BF_FIELD(rxwm, 1, 1);
END_BF_DECL();
static inline sckdiv_t& sckdiv_reg(){
return *reinterpret_cast<sckdiv_t*>(BASE_ADDR+SPI_REG_SCKDIV);
}
static inline sckmode_t& sckmode_reg(){
return *reinterpret_cast<sckmode_t*>(BASE_ADDR+SPI_REG_SCKMODE);
}
static inline uint32_t& csid_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+SPI_REG_CSID);
}
static inline uint32_t& csdef_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+SPI_REG_CSDEF);
}
static inline csmode_t& csmode_reg(){
return *reinterpret_cast<csmode_t*>(BASE_ADDR+SPI_REG_CSMODE);
}
static inline delay0_t& dcssck_reg(){
return *reinterpret_cast<delay0_t*>(BASE_ADDR+SPI_REG_DCSSCK);
}
static inline uint32_t& dsckcs_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+SPI_REG_DSCKCS);
}
static inline delay1_t& dintercs_reg(){
return *reinterpret_cast<delay1_t*>(BASE_ADDR+SPI_REG_DINTERCS);
}
static inline uint32_t& dinterxfr_reg(){
return *reinterpret_cast<uint32_t*>(BASE_ADDR+SPI_REG_DINTERXFR);
}
static inline fmt_t& fmt_reg(){
return *reinterpret_cast<fmt_t*>(BASE_ADDR+SPI_REG_FMT);
}
static inline txdata_t& txfifo_reg(){
return *reinterpret_cast<txdata_t*>(BASE_ADDR+SPI_REG_TXFIFO);
}
static inline rxdata_t& rxfifo_reg(){
return *reinterpret_cast<rxdata_t*>(BASE_ADDR+SPI_REG_RXFIFO);
}
static inline txmark_t& txctrl_reg(){
return *reinterpret_cast<txmark_t*>(BASE_ADDR+SPI_REG_TXCTRL);
}
static inline rxmark_t& rxctrl_reg(){
return *reinterpret_cast<rxmark_t*>(BASE_ADDR+SPI_REG_RXCTRL);
}
static inline fctrl_t& fctrl_reg(){
return *reinterpret_cast<fctrl_t*>(BASE_ADDR+SPI_REG_FCTRL);
}
static inline ffmt_t& ffmt_reg(){
return *reinterpret_cast<ffmt_t*>(BASE_ADDR+SPI_REG_FFMT);
}
static inline ie_t& ie_reg(){
return *reinterpret_cast<ie_t*>(BASE_ADDR+SPI_REG_IE);
}
static inline ip_t& ip_reg(){
return *reinterpret_cast<ip_t*>(BASE_ADDR+SPI_REG_IP);
}
template<size_t SIZE>
static bool transfer(std::array<uint8_t, SIZE>& bytes){
csmode_reg().mode=2; // HOLD mode
rxctrl_reg().rxmark=bytes.size()-1; // trigger irq if more than 2 bytes are received;
ie_reg().rxwm=1;
// write data bytes
for(size_t i=0; i<bytes.size(); ++i)
txfifo_reg()=bytes[i];
// wait until SPI is done
spi_active=true;
do{
asm("wfi");
asm("nop");
}while(spi_active);
// deactivate SPI
csmode_reg().mode=0; // AUTO mode, deactivates CS
// fetch results
for(size_t i=0; i<bytes.size(); ++i) bytes[i]=rxfifo_reg();
return true;
}
static void spi_rx_interrupt_handler(){
ip_reg().rxwm=0;
ie_reg().rxwm=0;
spi_active=false;
}
private:
static volatile bool spi_active;
};
#endif /* SPI_H_ */

View File

@ -0,0 +1,83 @@
/*
* spi.h
*
* Created on: 29.07.2018
* Author: eyck
*/
#ifndef UART_H_
#define UART_H_
#include <sifive/devices/uart.h>
#include "util/bit_field.h"
#include <cstdint>
template<uint32_t BASE_ADDR>
class uart_regs {
public:
BEGIN_BF_DECL(txdata_t, uint32_t);
BF_FIELD(data, 0, 8);
BF_FIELD(full, 31, 1);
END_BF_DECL() ;
BEGIN_BF_DECL(rxdata_t, uint32_t);
BF_FIELD(data, 0, 8);
BF_FIELD(empty, 31, 1);
END_BF_DECL();
BEGIN_BF_DECL(txctrl_t, uint32_t);
BF_FIELD(txen, 0, 1);
BF_FIELD(nstop, 1, 1);
BF_FIELD(txcnt, 16, 3);
END_BF_DECL();
BEGIN_BF_DECL(rxctrl_t, uint32_t);
BF_FIELD(rxen, 0, 1);
BF_FIELD(rxcnt, 16, 3);
END_BF_DECL();
BEGIN_BF_DECL(ie_t, uint32_t);
BF_FIELD(txwm, 0, 1);
BF_FIELD(rxwm, 1, 1);
END_BF_DECL();
BEGIN_BF_DECL(ip_t, uint32_t);
BF_FIELD(txwm, 0, 1);
BF_FIELD(rxwm, 1, 1);
END_BF_DECL();
BEGIN_BF_DECL(div_t, uint32_t);
BF_FIELD(div, 0, 16);
END_BF_DECL();
static inline txdata_t& txdata_reg(){
return *reinterpret_cast<txdata_t*>(BASE_ADDR+UART_REG_TXFIFO);
}
static inline rxdata_t& rxdata_reg(){
return *reinterpret_cast<rxdata_t*>(BASE_ADDR+UART_REG_RXFIFO);
}
static inline txctrl_t& txctrl_reg(){
return *reinterpret_cast<txctrl_t*>(BASE_ADDR+UART_REG_TXCTRL);
}
static inline rxctrl_t& rxctrl_reg(){
return *reinterpret_cast<rxctrl_t*>(BASE_ADDR+UART_REG_RXCTRL);
}
static inline ie_t& ie_reg(){
return *reinterpret_cast<ie_t*>(BASE_ADDR+UART_REG_IE);
}
static inline ip_t& ip_reg(){
return *reinterpret_cast<ip_t*>(BASE_ADDR+UART_REG_IP);
}
static inline div_t& div_reg(){
return *reinterpret_cast<div_t*>(BASE_ADDR+UART_REG_DIV);
}
};
#endif /* SPI_H_ */

View File

@ -0,0 +1,250 @@
//============================================================================
// Name : riscv-bldc.cpp
// Author : Eyck Jentzsch
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include "riscv-bldc.h"
#include "delay.h"
#include "bsp.h"
#include "plic/plic_driver.h"
#include <cstdio>
#include <cstdint>
#include "hifive1_io.h"
volatile uint32_t nextCommutationStep=0;
/* commutation blocks
* 1 2 3 4 5 6
* U 0 z +1 +1 z 0
* V +1 +1 z 0 0 z
* W z 0 0 z +1 +1
*/
std::array<uint32_t, 6> driveTable { //! Drive pattern for commutation, CW rotation
((1 << VH) | (1 << UL)), //1
((1 << VH) | (1 << WL)), //2
((1 << UH) | (1 << WL)), //3
((1 << UH) | (1 << VL)), //4
((1 << WH) | (1 << VL)), //5
((1 << WH) | (1 << UL)) //6
};
std::array<uint32_t, 6> senseTable { //! channels to sense during the applied pattern
SENSW_N, //1
SENSU_P, //2
SENSV_N, //3
SENSW_P, //4
SENSU_N, //5
SENSV_P //6
};
bool ccw=false;
typedef void (*function_ptr_t) (void);
//! Instance data for the PLIC.
plic_instance_t g_plic;
std::array<function_ptr_t,PLIC_NUM_INTERRUPTS> g_ext_interrupt_handlers;
/*! \brief external interrupt handler
*
* routes the peripheral interrupts to the the respective handler
*
*/
extern "C" void handle_m_ext_interrupt() {
plic_source int_num = PLIC_claim_interrupt(&g_plic);
if ((int_num >=1 ) && (int_num < PLIC_NUM_INTERRUPTS))
g_ext_interrupt_handlers[int_num]();
//else
// exit(1 + (uintptr_t) int_num);
PLIC_complete_interrupt(&g_plic, int_num);
}
/*! \brief mtime interval interrupt
*
*/
extern "C" void handle_m_time_interrupt(){
clear_csr(mie, MIP_MTIP);
// Reset the timer for 3s in the future.
// This also clears the existing timer interrupt.
volatile uint64_t * mtime = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIME);
volatile uint64_t * mtimecmp = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
uint64_t now = *mtime;
uint64_t then = now + RTC_FREQ;
*mtimecmp = then;
// Re-enable the timer interrupt.
set_csr(mie, MIP_MTIP);
}
/*! \brief dummy interrupt handler
*
*/
void no_interrupt_handler (void) {};
/*! \brief configure the per-interrupt handler
*
*/
void configure_irq(size_t irq_num, function_ptr_t handler, unsigned char prio=1) {
g_ext_interrupt_handlers[irq_num] = handler;
// Priority must be set > 0 to trigger the interrupt.
PLIC_set_priority(&g_plic, irq_num, prio);
// Have to enable the interrupt both at the GPIO level, and at the PLIC level.
PLIC_enable_interrupt(&g_plic, irq_num);
}
/*!\brief initializes platform
*
*/
void platform_init(){
// configure clocks
PRCI_use_hfxosc(1); // is equivalent to
// init UART0 at 115200 baud
auto baud_rate=115200;
gpio0::output_en_reg()=0xffffffff;
gpio0::iof_sel_reg()&=~IOF0_UART0_MASK;
gpio0::iof_en_reg()|= IOF0_UART0_MASK;
uart0::div_reg()=get_cpu_freq() / baud_rate - 1;
uart0::txctrl_reg().txen=1;
// init SPI
gpio0::iof_sel_reg()&=~IOF0_SPI1_MASK;
gpio0::iof_en_reg()|= IOF0_SPI1_MASK;
qspi1::sckdiv_reg() = 8;
F_CPU=PRCI_measure_mcycle_freq(20, RTC_FREQ);
printf("core freq at %d Hz\n", F_CPU);
// initialie interupt & trap handling
write_csr(mtvec, &trap_entry);
if (read_csr(misa) & (1 << ('F' - 'A'))) { // if F extension is present
write_csr(mstatus, MSTATUS_FS); // allow FPU instructions without trapping
write_csr(fcsr, 0); // initialize rounding mode, undefined at reset
}
PLIC_init(&g_plic, PLIC_CTRL_ADDR, PLIC_NUM_INTERRUPTS, PLIC_NUM_PRIORITIES);
// Disable the machine & timer interrupts until setup is done.
clear_csr(mie, MIP_MEIP);
clear_csr(mie, MIP_MTIP);
for (auto& h:g_ext_interrupt_handlers) h=no_interrupt_handler;
configure_irq(40, pwm0::pwm_interrupt_handler);
configure_irq(6, qspi1::spi_rx_interrupt_handler);
// Set the machine timer to go off in 1 second.
volatile uint64_t * mtime = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIME);
volatile uint64_t * mtimecmp = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
uint64_t now = *mtime;
uint64_t then = now + RTC_FREQ;
*mtimecmp = then;
// Enable the Machine-External bit in MIE
set_csr(mie, MIP_MEIP);
// Enable the Machine-Timer bit in MIE
set_csr(mie, MIP_MTIP);
// Enable interrupts in general.
set_csr(mstatus, MSTATUS_MIE);
}
/*! \brief reads adc channel and returns measured value
*
*/
unsigned read_adc(unsigned channel){
std::array<uint8_t, 3> bytes{
uint8_t(0x06 | (channel>>2 & 0x1)), /* start bit, single ended measurement, channel[2] */
uint8_t((channel&0x3)<<6), /* channel[1:0], fill*/
0x0 /* fill */
};
// set CS of target
qspi1::csid_reg()=0;
qspi1::transfer(bytes);
return (bytes[1]&0xf)*256+bytes[2];
}
/*! \brief waits for zero crossing and measures time until
*
*/
unsigned short measured_zc_time(unsigned short max_delay){
long delay_us = max_delay;
auto scaling_factor=0;
while(delay_us/(1<<scaling_factor) > std::numeric_limits<unsigned short>::max()){
scaling_factor++;
}
pwm0::cfg_reg()=0;
pwm0::count_reg()=0;
pwm0::cfg_reg().scale = 4+scaling_factor; // divide by 16 so we get 1us per pwm clock
pwm0::cmp0_reg().cmp0 = delay_us/(1<<scaling_factor);
pwm0::set_active();
pwm0::cfg_reg().enoneshot=true;
uint32_t channel=senseTable[nextCommutationStep]&0x3;
bool zc_neg = senseTable[nextCommutationStep]>3;
uint32_t adc_res=0;
do{
adc_res=read_adc(channel);
if((zc_neg && adc_res<2048) || (!zc_neg && adc_res>2047))
break;
} while(pwm0::is_active());
uint32_t sreg = pwm0::s_reg();
pwm0::cfg_reg().enoneshot=false;
return sreg*(1<<scaling_factor);
}
/*! \brief calculates the next commutation step
*
*/
inline void next_commutation_step(void) {
if (ccw) {
nextCommutationStep = nextCommutationStep == 0? 5 : nextCommutationStep-1;
} else {
nextCommutationStep = nextCommutationStep == 5? 0 : nextCommutationStep+1;
}
}
/*! \brief write the drive pattern to gpio
*
*/
void setDrivePattern(uint32_t nextDrivePattern) {
gpio0::port_reg() = (gpio0::port_reg() & ~DRIVE_MASK & 0x00ffffff) | nextDrivePattern | nextCommutationStep << 24;
}
/*! \brief open-loop commutation to start the motor
*
*/
void start_open_loop(void){
auto delay = 30120U;
std::array<double, 2> multiplier={0.83, 1.0};
nextCommutationStep = 0;
//Preposition.
gpio0::port_reg() = (gpio0::port_reg() & ~DRIVE_MASK) | driveTable[nextCommutationStep];
//fixed_delay(STARTUP_LOCK_DELAY);
pwm0::oneshot_delay(STARTUP_DELAY);
next_commutation_step();
auto nextDrivePattern = driveTable[nextCommutationStep];
for (size_t i = 0; i < 12; i++){
setDrivePattern(nextDrivePattern);
auto channel=senseTable[nextCommutationStep]&0x3;
auto zcPolRise = senseTable[nextCommutationStep]<4;
auto bemf_0=read_adc(channel);
delay*=multiplier[(i/6)%multiplier.size()];
pwm0::oneshot_delay(delay);
auto bemf_1=read_adc(channel);
auto bemf = bemf_1>bemf_0?bemf_1-bemf_0:bemf_0-bemf_1;
next_commutation_step();
nextDrivePattern = driveTable[nextCommutationStep];
}
}
/*! \brief closed-loop commutation to run the motor
*
*/
void run_closed_loop(void){
auto count=0;
auto zc_delay=0U;
auto tmp=0U;
auto nextDrivePattern = driveTable[nextCommutationStep];
for(;;){
setDrivePattern(nextDrivePattern);
zc_delay=measured_zc_time(50000);
next_commutation_step();
nextDrivePattern = driveTable[nextCommutationStep];
}
}
/*! \brief main function
*
*/
int main() {
platform_init();
printf("Starting motor\n");
start_open_loop();
printf("done...\n");
// Switch to sensor-less closed-loop commutation.
run_closed_loop();
return 0;
}

View File

@ -0,0 +1,41 @@
/*
* riscv-bldc.h
*
* Created on: 28.07.2018
* Author: eyck
*/
#ifndef RISCV_BLDC_H_
#define RISCV_BLDC_H_
#include <cstdint>
extern uint32_t pwm;
extern uint32_t DRIVE_PORT;
enum {
UL=1, //! Port pin connected to phase U, low side enable switch.
UH=0, //! Port pin connected to phase U, high side enable switch.
VL=11, //! Port pin connected to phase V, low side enable switch.
VH=10, //! Port pin connected to phase V, high side enable switch.
WL=19, //! Port pin connected to phase W, low side enable switch.
WH=20, //! Port pin connected to phase W, high side enable switch.
SENSU_P=0, //! Phase U voltage to sense positive zero cross
SENSV_P=1, //! Phase V voltage to sense positive zero cross
SENSW_P=2, //! Phase W voltage to sense positive zero cross
SENSU_N=4, //! Phase U voltage to sense negative zero cross
SENSV_N=5, //! Phase V voltage to sense negative zero cross
SENSW_N=6, //! Phase W voltage to sense negative zero cross
DRIVE_MASK=(1<<UL)|(1<<UH)| (1<<VL)|(1<<VH)| (1<<WL)|(1<<WH)
};
/*!
* Number of 10 microseconds to lock rotor in first commutation step before
* the timed startup sequence is initiated.
*/
const auto STARTUP_DELAY=25000U;
extern "C" void handle_m_ext_interrupt();
extern "C" void handle_m_time_interrupt();
#endif /* RISCV_BLDC_H_ */

View File

@ -0,0 +1,179 @@
/*---------------------------------------------------------
Copyright (c) 2015 Jeff Preshing
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgement in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
---------------------------------------------------------*/
#ifndef BIT_FIELD_H_
#define BIT_FIELD_H_
#ifndef __CPP11OM_BITFIELD_H__
#define __CPP11OM_BITFIELD_H__
#include <cassert>
//---------------------------------------------------------
// BitFieldMember<>: Used internally by ADD_BITFIELD_MEMBER macro.
// All members are public to simplify compliance with sections 9.0.7 and
// 9.5.1 of the C++11 standard, thereby avoiding undefined behavior.
//---------------------------------------------------------
template <typename T, int Offset, int Bits> struct BitFieldMember {
T value;
static_assert(Offset + Bits <= (int)sizeof(T) * 8, "Member exceeds bitfield boundaries");
static_assert(Bits < (int)sizeof(T) * 8, "Can't fill entire bitfield with one member");
static const T Maximum = (T(1) << Bits) - 1;
static const T Mask = Maximum << Offset;
T maximum() const { return Maximum; }
T one() const { return T(1) << Offset; }
operator T() const { return (value >> Offset) & Maximum; }
BitFieldMember &operator=(T v) {
assert(v <= Maximum); // v must fit inside the bitfield member
value = (value & ~Mask) | (v << Offset);
return *this;
}
BitFieldMember &operator+=(T v) {
assert(T(*this) + v <= Maximum); // result must fit inside the bitfield member
value += v << Offset;
return *this;
}
BitFieldMember &operator-=(T v) {
assert(T(*this) >= v); // result must not underflow
value -= v << Offset;
return *this;
}
BitFieldMember &operator++() { return *this += 1; }
BitFieldMember operator++(int) { // postfix form
BitFieldMember tmp(*this);
operator++();
return tmp;
}
BitFieldMember &operator--() { return *this -= 1; }
BitFieldMember operator--(int) { // postfix form
BitFieldMember tmp(*this);
operator--();
return tmp;
}
};
//---------------------------------------------------------
// BitFieldArray<>: Used internally by ADD_BITFIELD_ARRAY macro.
// All members are public to simplify compliance with sections 9.0.7 and
// 9.5.1 of the C++11 standard, thereby avoiding undefined behavior.
//---------------------------------------------------------
template <typename T, int BaseOffset, int BitsPerItem, int NumItems> class BitFieldArray {
public:
T value;
static_assert(BaseOffset + BitsPerItem * NumItems <= (int)sizeof(T) * 8, "Array exceeds bitfield boundaries");
static_assert(BitsPerItem < (int)sizeof(T) * 8, "Can't fill entire bitfield with one array element");
static const T Maximum = (T(1) << BitsPerItem) - 1;
T maximum() const { return Maximum; }
int numItems() const { return NumItems; }
class Element {
private:
T &value;
int offset;
public:
Element(T &value, int offset)
: value(value)
, offset(offset) {}
T mask() const { return Maximum << offset; }
operator T() const { return (value >> offset) & Maximum; }
Element &operator=(T v) {
assert(v <= Maximum); // v must fit inside the bitfield member
value = (value & ~mask()) | (v << offset);
return *this;
}
Element &operator+=(T v) {
assert(T(*this) + v <= Maximum); // result must fit inside the bitfield member
value += v << offset;
return *this;
}
Element &operator-=(T v) {
assert(T(*this) >= v); // result must not underflow
value -= v << offset;
return *this;
}
Element &operator++() { return *this += 1; }
Element operator++(int) { // postfix form
Element tmp(*this);
operator++();
return tmp;
}
Element &operator--() { return *this -= 1; }
Element operator--(int) { // postfix form
Element tmp(*this);
operator--();
return tmp;
}
};
Element operator[](int i) {
assert(i >= 0 && i < NumItems); // array index must be in range
return Element(value, BaseOffset + BitsPerItem * i);
}
const Element operator[](int i) const {
assert(i >= 0 && i < NumItems); // array index must be in range
return Element(value, BaseOffset + BitsPerItem * i);
}
};
//---------------------------------------------------------
// Bitfield definition macros.
// All members are public to simplify compliance with sections 9.0.7 and
// 9.5.1 of the C++11 standard, thereby avoiding undefined behavior.
//---------------------------------------------------------
#define BEGIN_BF_DECL(typeName, T) \
union typeName { \
struct Wrapper { \
T value; \
}; \
Wrapper flat; \
typeName(T v = 0) { flat.value = v; } \
typeName &operator=(T v) { \
flat.value = v; \
return *this; \
} \
operator T &() { return flat.value; } \
operator T() const { return flat.value; } \
using StorageType = T;
#define BF_FIELD(memberName, offset, bits) BitFieldMember<StorageType, offset, bits> memberName;
#define BF_ARRAY(memberName, offset, bits, numItems) BitFieldArray<StorageType, offset, bits, numItems> memberName;
#define END_BF_DECL() }
#endif // __CPP11OM_BITFIELD_H__
#endif /* BIT_FIELD_H_ */

View File

@ -0,0 +1,271 @@
/* The functions in this file are only meant to support Dhrystone on an
* embedded RV32 system and are obviously incorrect in general. */
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#undef putchar
int putchar(int ch)
{
return write(STDOUT_FILENO, &ch, 1) == 1 ? ch : -1;
}
static void sprintf_putch(int ch, void** data)
{
char** pstr = (char**)data;
**pstr = ch;
(*pstr)++;
}
static unsigned long getuint(va_list *ap, int lflag)
{
if (lflag)
return va_arg(*ap, unsigned long);
else
return va_arg(*ap, unsigned int);
}
static long getint(va_list *ap, int lflag)
{
if (lflag)
return va_arg(*ap, long);
else
return va_arg(*ap, int);
}
static inline void printnum(void (*putch)(int, void**), void **putdat,
unsigned long num, unsigned base, int width, int padc)
{
unsigned digs[sizeof(num)*8];
int pos = 0;
while (1)
{
digs[pos++] = num % base;
if (num < base)
break;
num /= base;
}
while (width-- > pos)
putch(padc, putdat);
while (pos-- > 0)
putch(digs[pos] + (digs[pos] >= 10 ? 'a' - 10 : '0'), putdat);
}
static inline void print_double(void (*putch)(int, void**), void **putdat,
double num, int width, int prec)
{
union {
double d;
uint64_t u;
} u;
u.d = num;
if (u.u & (1ULL << 63)) {
putch('-', putdat);
u.u &= ~(1ULL << 63);
}
for (int i = 0; i < prec; i++)
u.d *= 10;
char buf[32], *pbuf = buf;
printnum(sprintf_putch, (void**)&pbuf, (unsigned long)u.d, 10, 0, 0);
if (prec > 0) {
for (int i = 0; i < prec; i++) {
pbuf[-i] = pbuf[-i-1];
}
pbuf[-prec] = '.';
pbuf++;
}
for (char* p = buf; p < pbuf; p++)
putch(*p, putdat);
}
static void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_list ap)
{
register const char* p;
const char* last_fmt;
register int ch, err;
unsigned long num;
int base, lflag, width, precision, altflag;
char padc;
while (1) {
while ((ch = *(unsigned char *) fmt) != '%') {
if (ch == '\0')
return;
fmt++;
putch(ch, putdat);
}
fmt++;
// Process a %-escape sequence
last_fmt = fmt;
padc = ' ';
width = -1;
precision = -1;
lflag = 0;
altflag = 0;
reswitch:
switch (ch = *(unsigned char *) fmt++) {
// flag to pad on the right
case '-':
padc = '-';
goto reswitch;
// flag to pad with 0's instead of spaces
case '0':
padc = '0';
goto reswitch;
// width field
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
for (precision = 0; ; ++fmt) {
precision = precision * 10 + ch - '0';
ch = *fmt;
if (ch < '0' || ch > '9')
break;
}
goto process_precision;
case '*':
precision = va_arg(ap, int);
goto process_precision;
case '.':
if (width < 0)
width = 0;
goto reswitch;
case '#':
altflag = 1;
goto reswitch;
process_precision:
if (width < 0)
width = precision, precision = -1;
goto reswitch;
// long flag
case 'l':
if (lflag)
goto bad;
goto reswitch;
// character
case 'c':
putch(va_arg(ap, int), putdat);
break;
// double
case 'f':
print_double(putch, putdat, va_arg(ap, double), width, precision);
break;
// string
case 's':
if ((p = va_arg(ap, char *)) == NULL)
p = "(null)";
if (width > 0 && padc != '-')
for (width -= strnlen(p, precision); width > 0; width--)
putch(padc, putdat);
for (; (ch = *p) != '\0' && (precision < 0 || --precision >= 0); width--) {
putch(ch, putdat);
p++;
}
for (; width > 0; width--)
putch(' ', putdat);
break;
// (signed) decimal
case 'd':
num = getint(&ap, lflag);
if ((long) num < 0) {
putch('-', putdat);
num = -(long) num;
}
base = 10;
goto signed_number;
// unsigned decimal
case 'u':
base = 10;
goto unsigned_number;
// (unsigned) octal
case 'o':
// should do something with padding so it's always 3 octits
base = 8;
goto unsigned_number;
// pointer
case 'p':
lflag = 1;
putch('0', putdat);
putch('x', putdat);
/* fall through to 'x' */
// (unsigned) hexadecimal
case 'x':
base = 16;
unsigned_number:
num = getuint(&ap, lflag);
signed_number:
printnum(putch, putdat, num, base, width, padc);
break;
// escaped '%' character
case '%':
putch(ch, putdat);
break;
// unrecognized escape sequence - just print it literally
default:
bad:
putch('%', putdat);
fmt = last_fmt;
break;
}
}
}
int __wrap_printf(const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintfmt((void*)putchar, 0, fmt, ap);
va_end(ap);
return 0; // incorrect return value, but who cares, anyway?
}
int __wrap_sprintf(char* str, const char* fmt, ...)
{
va_list ap;
char* str0 = str;
va_start(ap, fmt);
vprintfmt(sprintf_putch, (void**)&str, fmt, ap);
*str = 0;
va_end(ap);
return str - str0;
}