mirror of
https://github.com/captain-amygdala/pistorm.git
synced 2026-04-16 00:20:51 +00:00
391 lines
11 KiB
C
391 lines
11 KiB
C
// SPDX-License-Identifier: MIT
|
|
|
|
/*
|
|
* Copyright 2022 Claude Schwarz
|
|
* Copyright 2022 Niklas Ekström
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "ps_protocol.h"
|
|
|
|
#define LOGGER_INFO 1
|
|
#define logger_info(...) do { if (LOGGER_INFO) fprintf(stdout, __VA_ARGS__); } while (0)
|
|
|
|
typedef unsigned int uint;
|
|
|
|
extern void m68k_pulse_bus_error(void);
|
|
//void m68k_pulse_bus_error(void) {}
|
|
|
|
|
|
#define PF(f, i) (f << ((i % 10) * 3)) // Pin function.
|
|
#define GO(i) PF(1, i) // GPIO output.
|
|
|
|
#define SPLIT_DATA(x) (x)
|
|
#define MERGE_DATA(x) (x)
|
|
|
|
#define GPFSEL0_INPUT (GO(PIN_WR) | GO(PIN_RD))
|
|
#define GPFSEL1_INPUT (0)
|
|
#define GPFSEL2_INPUT (GO(PIN_A(2)) | GO(PIN_A(1)) | GO(PIN_A(0)))
|
|
|
|
#define GPFSEL0_OUTPUT (GO(PIN_D(1)) | GO(PIN_D(0)) | GO(PIN_WR) | GO(PIN_RD))
|
|
#define GPFSEL1_OUTPUT (GO(PIN_D(11)) | GO(PIN_D(10)) | GO(PIN_D(9)) | GO(PIN_D(8)) | GO(PIN_D(7)) | GO(PIN_D(6)) | GO(PIN_D(5)) | GO(PIN_D(4)) | GO(PIN_D(3)) | GO(PIN_D(2)))
|
|
#define GPFSEL2_OUTPUT (GO(PIN_A(2)) | GO(PIN_A(1)) | GO(PIN_A(0)) | GO(PIN_D(15)) | GO(PIN_D(14)) | GO(PIN_D(13)) | GO(PIN_D(12)))
|
|
|
|
#define REG_DATA_LO 0
|
|
#define REG_DATA_HI 1
|
|
#define REG_ADDR_LO 2
|
|
#define REG_ADDR_HI 3
|
|
#define REG_STATUS 4
|
|
#define REG_CONTROL 4
|
|
|
|
#define TXN_SIZE_SHIFT 8
|
|
#define TXN_RW_SHIFT 10
|
|
#define TXN_FC_SHIFT 11
|
|
|
|
#define SIZE_BYTE 0
|
|
#define SIZE_WORD 1
|
|
#define SIZE_LONG 3
|
|
|
|
#define TXN_READ (1 << TXN_RW_SHIFT)
|
|
#define TXN_WRITE (0 << TXN_RW_SHIFT)
|
|
|
|
#define GPIO_ADDR 0x200000
|
|
#define GPCLK_ADDR 0x101000
|
|
|
|
#define CLK_PASSWD 0x5a000000
|
|
#define CLK_GP0_CTL 0x070
|
|
#define CLK_GP0_DIV 0x074
|
|
|
|
#define GPIO_PULL *(gpio + 37) // Pull up/pull down
|
|
#define GPIO_PULLCLK0 *(gpio + 38) // Pull up/pull down clock
|
|
|
|
#define DIRECTION_INPUT 0
|
|
#define DIRECTION_OUTPUT 1
|
|
|
|
volatile unsigned int *gpio;
|
|
unsigned int g_polltxn = 1;
|
|
volatile unsigned int *gpset;
|
|
volatile unsigned int *gpreset;
|
|
volatile unsigned int *gpread;
|
|
|
|
|
|
static void create_dev_mem_mapping() {
|
|
|
|
int fd = open("/dev/gpiomem", O_RDWR | O_SYNC);
|
|
if (fd < 0) {
|
|
logger_info("Unable to open /dev/gpiomem.\n");
|
|
exit(-1);
|
|
}
|
|
|
|
void *gpio_map = mmap(
|
|
NULL, // Any adddress in our space will do
|
|
(4*1024), // Map length
|
|
PROT_READ | PROT_WRITE, // Enable reading & writting to mapped memory
|
|
MAP_SHARED, // Shared with other processes
|
|
fd, // File to map
|
|
0 // Offset to GPIO peripheral
|
|
);
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
if (gpio_map == MAP_FAILED) {
|
|
logger_info("mmap failed, errno = %d\n", errno);
|
|
exit(-1);
|
|
}
|
|
|
|
gpio = ((volatile unsigned *)gpio_map);
|
|
gpset = ((volatile unsigned *)gpio) + 7;// *(gpio + 7);
|
|
gpreset = ((volatile unsigned *)gpio) + 10;// *(gpio + 10);
|
|
gpread = ((volatile unsigned *)gpio) + 13;// *(gpio + 13);
|
|
|
|
}
|
|
|
|
static inline void set_input() {
|
|
*(gpio + 0) = GPFSEL0_INPUT;
|
|
*(gpio + 1) = GPFSEL1_INPUT;
|
|
*(gpio + 2) = GPFSEL2_INPUT;
|
|
}
|
|
|
|
static inline void set_output() {
|
|
*(gpio + 0) = GPFSEL0_OUTPUT;
|
|
*(gpio + 1) = GPFSEL1_OUTPUT;
|
|
*(gpio + 2) = GPFSEL2_OUTPUT;
|
|
}
|
|
|
|
void ps_setup_protocol() {
|
|
create_dev_mem_mapping();
|
|
|
|
*(gpio + 10) = 0x0fffff3f;
|
|
|
|
set_input();
|
|
}
|
|
|
|
static inline void write_ps_reg(unsigned int address, unsigned int data) {
|
|
*(gpset) = (SPLIT_DATA(data) << PIN_D(0)) | (address << PIN_A(0));
|
|
//Delay for Pi4, 2*3.5nS
|
|
*(gpreset) = 1 << PIN_WR;*(gpreset) = 1 << PIN_WR;*(gpreset) = (1 << PIN_WR) ;
|
|
*(gpset) = 1 << PIN_WR;
|
|
*(gpreset) = 0x0fffff3f;
|
|
}
|
|
|
|
static inline unsigned int read_ps_reg(unsigned int address) {
|
|
*(gpset) = (address << PIN_A(0));
|
|
//Delay for Pi3, 3*7.5nS , or 3*3.5nS for
|
|
*(gpreset) = 1 << PIN_RD;*(gpreset) = 1 << PIN_RD;*(gpreset) = 1 << PIN_RD;*(gpreset) = (1 << PIN_RD);
|
|
|
|
unsigned int data = *(gpread);
|
|
*(gpset) = (1 << PIN_RD);
|
|
*(gpreset) = 0x0fffff3f;
|
|
|
|
data = MERGE_DATA(data >> PIN_D(0)) & 0xffff;
|
|
return data;
|
|
}
|
|
|
|
|
|
void ps_set_control(unsigned int value) {
|
|
set_output();
|
|
write_ps_reg(REG_CONTROL, 0x8000 | (value & 0x7fff));
|
|
set_input();
|
|
}
|
|
|
|
void ps_clr_control(unsigned int value) {
|
|
set_output();
|
|
write_ps_reg(REG_CONTROL, value & 0x7fff);
|
|
set_input();
|
|
}
|
|
|
|
unsigned int ps_read_status() {
|
|
return read_ps_reg(REG_STATUS);
|
|
}
|
|
|
|
static uint g_fc = 0;
|
|
|
|
void cpu_set_fc(unsigned int fc) {
|
|
g_fc = fc;
|
|
}
|
|
|
|
void ps_write_8(unsigned int address, unsigned int data) {
|
|
set_output();
|
|
write_ps_reg(REG_DATA_LO, data & 0xffff);
|
|
write_ps_reg(REG_ADDR_LO, address & 0xffff);
|
|
if(g_polltxn) while (*(gpread) & (1 << PIN_TXN)) {}
|
|
write_ps_reg(REG_ADDR_HI, TXN_WRITE | (g_fc << TXN_FC_SHIFT) | (SIZE_BYTE << TXN_SIZE_SHIFT) | ((address >> 16) & 0xff));
|
|
set_input();
|
|
g_polltxn=1;
|
|
}
|
|
|
|
void ps_write_16(unsigned int address, unsigned int data) {
|
|
set_output();
|
|
write_ps_reg(REG_DATA_LO, data & 0xffff);
|
|
write_ps_reg(REG_ADDR_LO, address & 0xffff);
|
|
if(g_polltxn) while (*(gpread) & (1 << PIN_TXN)) {}
|
|
write_ps_reg(REG_ADDR_HI, TXN_WRITE | (g_fc << TXN_FC_SHIFT) | (SIZE_WORD << TXN_SIZE_SHIFT) | ((address >> 16) & 0xff));
|
|
set_input();
|
|
g_polltxn=1;
|
|
}
|
|
|
|
void ps_write_32(unsigned int address, unsigned int data) {
|
|
set_output();
|
|
write_ps_reg(REG_DATA_LO, data & 0xffff);
|
|
write_ps_reg(REG_DATA_HI, (data >> 16) & 0xffff);
|
|
write_ps_reg(REG_ADDR_LO, address & 0xffff);
|
|
if(g_polltxn) while (*(gpread) & (1 << PIN_TXN)) {}
|
|
write_ps_reg(REG_ADDR_HI, TXN_WRITE | (g_fc << TXN_FC_SHIFT) | (SIZE_LONG << TXN_SIZE_SHIFT) | ((address >> 16) & 0xff));
|
|
set_input();
|
|
g_polltxn=1;
|
|
}
|
|
|
|
unsigned int ps_read_8(unsigned int address) {
|
|
set_output();
|
|
write_ps_reg(REG_ADDR_LO, address & 0xffff);
|
|
if(g_polltxn) while (*(gpread) & (1 << PIN_TXN)) {}
|
|
write_ps_reg(REG_ADDR_HI, TXN_READ | (g_fc << TXN_FC_SHIFT) | (SIZE_BYTE << TXN_SIZE_SHIFT) | ((address >> 16) & 0xff));
|
|
set_input();
|
|
while (*(gpread) & (1 << PIN_TXN)) {}
|
|
unsigned int data = read_ps_reg(REG_DATA_LO);
|
|
data &= 0xff;
|
|
g_polltxn=0;
|
|
|
|
return data;
|
|
}
|
|
|
|
unsigned int ps_read_16(unsigned int address) {
|
|
set_output();
|
|
write_ps_reg(REG_ADDR_LO, address & 0xffff);
|
|
if(g_polltxn) while (*(gpread) & (1 << PIN_TXN)) {}
|
|
write_ps_reg(REG_ADDR_HI, TXN_READ | (g_fc << TXN_FC_SHIFT) | (SIZE_WORD << TXN_SIZE_SHIFT) | ((address >> 16) & 0xff));
|
|
set_input();
|
|
while (*(gpread) & (1 << PIN_TXN)) {}
|
|
unsigned int data = read_ps_reg(REG_DATA_LO);
|
|
g_polltxn=0;
|
|
|
|
return data;
|
|
}
|
|
|
|
unsigned int ps_read_32(unsigned int address) {
|
|
set_output();
|
|
write_ps_reg(REG_ADDR_LO, address & 0xffff);
|
|
if(g_polltxn) while (*(gpread) & (1 << PIN_TXN)) {}
|
|
write_ps_reg(REG_ADDR_HI, TXN_READ | (g_fc << TXN_FC_SHIFT) | (SIZE_LONG << TXN_SIZE_SHIFT) | ((address >> 16) & 0xff));
|
|
set_input();
|
|
while (*(gpread) & (1 << PIN_TXN)) {}
|
|
unsigned int data = read_ps_reg(REG_DATA_LO);
|
|
data |= read_ps_reg(REG_DATA_HI) << 16;
|
|
g_polltxn=0;
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
|
|
static void do_write_access(unsigned int address, unsigned int data, unsigned int size) {
|
|
|
|
set_output();
|
|
|
|
write_ps_reg(REG_DATA_LO, data & 0xffff);
|
|
if (size == SIZE_LONG)
|
|
write_ps_reg(REG_DATA_HI, (data >> 16) & 0xffff);
|
|
|
|
write_ps_reg(REG_ADDR_LO, address & 0xffff);
|
|
write_ps_reg(REG_ADDR_HI, TXN_WRITE | (g_fc << TXN_FC_SHIFT) | (size << TXN_SIZE_SHIFT) | ((address >> 16) & 0xff));
|
|
|
|
set_input();
|
|
while (*(gpio + 13) & (1 << PIN_TXN)) {}
|
|
}
|
|
|
|
static int do_read_access(unsigned int address, unsigned int size) {
|
|
|
|
if (address == 0xf00000)
|
|
usleep(1000);
|
|
|
|
set_output();
|
|
|
|
write_ps_reg(REG_ADDR_LO, address & 0xffff);
|
|
write_ps_reg(REG_ADDR_HI, TXN_READ | (g_fc << TXN_FC_SHIFT) | (size << TXN_SIZE_SHIFT) | ((address >> 16) & 0xff));
|
|
|
|
set_input();
|
|
unsigned int data;
|
|
|
|
while (*(gpio + 13) & (1 << PIN_TXN)) {}
|
|
|
|
data = read_ps_reg(REG_DATA_LO);
|
|
if (size == SIZE_BYTE)
|
|
data &= 0xff;
|
|
else if (size == SIZE_LONG)
|
|
data |= read_ps_reg(REG_DATA_HI) << 16;
|
|
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
void ps_write_8(unsigned int address, unsigned int data) {
|
|
do_write_access(address, data, SIZE_BYTE);
|
|
}
|
|
|
|
void ps_write_16(unsigned int address, unsigned int data) {
|
|
do_write_access(address, data, SIZE_WORD);
|
|
}
|
|
|
|
void ps_write_32(unsigned int address, unsigned int data) {
|
|
do_write_access(address, data, SIZE_LONG);
|
|
}
|
|
|
|
unsigned int ps_read_8(unsigned int address) {
|
|
return do_read_access(address, SIZE_BYTE);
|
|
}
|
|
|
|
unsigned int ps_read_16(unsigned int address) {
|
|
return do_read_access(address, SIZE_WORD);
|
|
}
|
|
|
|
unsigned int ps_read_32(unsigned int address) {
|
|
return do_read_access(address, SIZE_LONG);
|
|
}
|
|
*/
|
|
|
|
void ps_reset_state_machine() {
|
|
}
|
|
|
|
void ps_pulse_reset() {
|
|
logger_info("Set REQUEST_BM\n");
|
|
ps_set_control(CONTROL_REQ_BM);
|
|
usleep(100000);
|
|
|
|
logger_info("Set DRIVE_RESET\n");
|
|
ps_set_control(CONTROL_DRIVE_RESET);
|
|
usleep(150000);
|
|
|
|
logger_info("Clear DRIVE_RESET\n");
|
|
ps_clr_control(CONTROL_DRIVE_RESET);
|
|
}
|
|
|
|
|
|
void ps_efinix_setup() {
|
|
//set programming pins to output
|
|
*(gpio + 0) = (GO(PIN_CRESET1) | GO(PIN_CRESET2) | GO(PIN_CDI2) | GO(PIN_CDI3) | GO(PIN_CDI5));//GPIO 0..9
|
|
*(gpio + 1) = (GO(PIN_CDI0) | GO(PIN_CDI4) | GO(PIN_CDI7) | GO(PIN_CDI6) | GO(PIN_CBUS0) | GO(PIN_CBUS1) | GO(PIN_CBUS2) | GO(PIN_TESTN));//GPIO 10..19
|
|
*(gpio + 2) = (GO(PIN_CCK) | GO(PIN_CDI1) | GO(PIN_SS)); //GPIO 20..29
|
|
|
|
//make sure FPGA is not in reset
|
|
*(gpio + 7) = (1 << PIN_CRESET1) | (1 << PIN_CRESET2) ;
|
|
|
|
/*
|
|
valid cbus options are
|
|
x1 SPI => CBUS 3'b111
|
|
x2 SPI => CBUS 3'b110
|
|
x4 SPI => CBUS 3'b101
|
|
x8 SPI => CBUS 3'b100
|
|
*/
|
|
//set cbus (x1 spi)
|
|
*(gpio + 7) = (1 << PIN_CBUS0) | (1 << PIN_CBUS1)| (1 << PIN_CBUS2);
|
|
|
|
//set other relevant pins for programming to correct level
|
|
*(gpio + 7) = (1 << PIN_TESTN) | (1 << PIN_CCK);
|
|
*(gpio + 10) = (1 << PIN_SS);
|
|
|
|
//reset fpga, latching cbus and mode pins
|
|
*(gpio + 10) = (1 << PIN_CRESET1) | (1 << PIN_CRESET2);
|
|
usleep(10000); //wait a bit for ps32-lite glitch filter RC to discharge
|
|
*(gpio + 7) = (1 << PIN_CRESET1) | (1 << PIN_CRESET2);
|
|
}
|
|
|
|
void ps_efinix_write(unsigned char data_out) {
|
|
//thats just bitbanged 1x SPI, takes ~100mS to load
|
|
unsigned char loop, mask;
|
|
for (loop=0,mask=0x80;loop<8;loop++, mask=mask>>1)
|
|
{
|
|
*(gpio + 10) = (1 << PIN_CCK);
|
|
if (data_out & mask) *(gpio + 7) = (1 << PIN_CDI0);
|
|
else *(gpio + 10) = (1 << PIN_CDI0);
|
|
*(gpio + 7) = (1 << PIN_CCK);
|
|
*(gpio + 7) = (1 << PIN_CCK); //to get closer to 50/50 duty cycle
|
|
}
|
|
*(gpio + 10) = (1 << PIN_CCK);
|
|
}
|
|
|
|
void ps_efinix_load(char* buffer, long length) {
|
|
long i;
|
|
logger_info("Loading FPGA\n");
|
|
for (i = 0; i < length; ++i)
|
|
{
|
|
ps_efinix_write(buffer[i]);
|
|
}
|
|
//1000 dummy clocks for startup of user logic
|
|
for (i = 0; i < 1000; ++i)
|
|
{
|
|
ps_efinix_write(0x00);
|
|
}
|
|
logger_info("FPGA loaded\n");
|
|
}
|