Delete unfinished HyperRAM code, it can be resurrected from the history at some point

two-pixels-per-word
Luke Wren 2021-02-18 19:40:08 +00:00
rodzic 14c27eaeae
commit 6895447bfd
8 zmienionych plików z 0 dodań i 350 usunięć

Wyświetl plik

@ -22,7 +22,6 @@ include_directories(
)
add_subdirectory(libdvi)
add_subdirectory(libhyperram)
add_subdirectory(libsprite)
add_subdirectory(apps)

Wyświetl plik

@ -2,7 +2,6 @@ add_subdirectory(bad_apple)
add_subdirectory(dht_logging)
add_subdirectory(dual_display)
add_subdirectory(hello_dvi)
add_subdirectory(hyperram_test)
add_subdirectory(mandelbrot)
add_subdirectory(moon)
add_subdirectory(sprite_bounce)

Wyświetl plik

@ -1,10 +0,0 @@
add_executable(hyperram_test
main.c
)
target_link_libraries(hyperram_test
pico_stdlib
libhyperram
)
pico_add_extra_outputs(hyperram_test)

Wyświetl plik

@ -1,67 +0,0 @@
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/vreg.h"
#include "hardware/clocks.h"
#include "hyperram.h"
#include "hyperram.pio.h"
void printdata(const uint8_t *data, uint len) {
for (int i = 0; i < len; ++i)
printf("%02x%c", data[i], i % 16 == 15 ? '\n' : ' ');
if (len % 16 != 15)
printf("\n");
}
#define TESTLEN 256
int main() {
vreg_set_voltage(VREG_VOLTAGE_1_20);
sleep_ms(1);
set_sys_clock_khz(252 * 1000, true);
setup_default_uart();
hyperram_inst_t hram = {
.pio = pio0,
.sm = 3,
.dq_base_pin = 22,
.ctrl_base_pin = 0,
.rst_n_pin = 4
};
printf("Loading program\n");
hram.prog_offset = pio_add_program(hram.pio, &hyperram_program);
printf("Initialising state machine\n");
hyperram_pio_init(&hram);
pio_sm_set_clkdiv(hram.pio, hram.sm, 50.f);
const uint16_t cfgreg_init =
(0x1u << 15) | // Power on (no DPDn)
(0x0u << 12) | // Default drive strength (34R)
(0xeu << 4) | // 3 latency cycles (in bias -5 format)
(0x1u << 3); // Fixed 2x latency mode
// Don't care about wrap modes, we're always linear
printf("Writing %04x to CR0 register\n", cfgreg_init);
hyperram_cfgreg_write(&hram, cfgreg_init);
for (int clkdiv = 10; clkdiv > 0; --clkdiv) {
printf("\nSetting clock divisor to %d\n", clkdiv);
pio_sm_set_clkdiv(hram.pio, hram.sm, clkdiv);
printf("Writing:\n");
uint8_t testdata[TESTLEN];
// Clear, then write test pattern
for (int i = 0; i < TESTLEN; ++i)
testdata[i] = 0;
hyperram_write_blocking(&hram, 0x1234, (const uint32_t*)testdata, TESTLEN / sizeof(uint32_t));
for (int i = 0; i < TESTLEN; ++i)
testdata[i] = i & 0xff;
// printdata(testdata, TESTLEN);
hyperram_write_blocking(&hram, 0x1234, (const uint32_t*)testdata, TESTLEN / sizeof(uint32_t));
printf("Reading back:\n");
hyperram_read_blocking(&hram, 0x1234, (uint32_t*)testdata, TESTLEN / sizeof(uint32_t));
printdata(testdata, TESTLEN);
}
}

Wyświetl plik

@ -1,18 +0,0 @@
# Note we are using INTERFACE so that the library can be configured per-app
# with compile-time defines
add_library(libhyperram INTERFACE)
target_sources(libhyperram INTERFACE
${CMAKE_CURRENT_LIST_DIR}/hyperram.c
${CMAKE_CURRENT_LIST_DIR}/hyperram.h
)
target_include_directories(libhyperram INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(libhyperram INTERFACE
pico_base_headers
hardware_dma
hardware_pio
)
pico_generate_pio_header(libhyperram ${CMAKE_CURRENT_LIST_DIR}/hyperram.pio)

Wyświetl plik

@ -1,156 +0,0 @@
#include "hardware/pio.h"
#include "hyperram.h"
#include "hyperram.pio.h"
#define CTRL_PIN_CS 0
#define CTRL_PIN_RWDS 1
#define CTRL_PIN_CK 2
void hyperram_pio_init(const hyperram_inst_t *inst) {
// Make sure HyperRAM is in reset before we futz with the GPIOs
gpio_init(inst->rst_n_pin);
gpio_put(inst->rst_n_pin, 0);
gpio_set_dir(inst->rst_n_pin, GPIO_OUT);
for (uint i = inst->dq_base_pin; i < inst->dq_base_pin + 8; ++i) {
// Setting both pull bits enables bus-keep function
gpio_set_pulls(i, true, true);
pio_gpio_init(inst->pio, i);
}
hw_clear_bits(&inst->pio->input_sync_bypass, 0xffu << inst->dq_base_pin);
for (uint i = inst->ctrl_base_pin; i < inst->ctrl_base_pin + 3; ++i)
pio_gpio_init(inst->pio, i);
gpio_pull_down(inst->ctrl_base_pin + CTRL_PIN_RWDS);
// All controls low except CSn
pio_sm_set_pins_with_mask(inst->pio, inst->sm,
(1u << CTRL_PIN_CS) << inst->ctrl_base_pin,
0x7u << inst->ctrl_base_pin
);
// All controls output except RWDS (DQs will sort themselves out later)
pio_sm_set_pindirs_with_mask(inst->pio, inst->sm,
(1u << CTRL_PIN_CS | 1u << CTRL_PIN_CK) << inst->ctrl_base_pin,
0x7u << inst->ctrl_base_pin
);
pio_sm_config c = hyperram_program_get_default_config(inst->prog_offset);
sm_config_set_out_pins(&c, inst->dq_base_pin, 8);
sm_config_set_in_pins(&c, inst->dq_base_pin);
sm_config_set_set_pins(&c, inst->ctrl_base_pin + CTRL_PIN_RWDS, 1);
sm_config_set_sideset_pins(&c, inst->ctrl_base_pin);
// Use shift-to-left (this means we write to memory in the wrong endianness,
// but we hide this by requiring word-aligned addresses)
sm_config_set_in_shift(&c, false, true, 32);
sm_config_set_out_shift(&c, false, true, 32);
// Should be removed: slow everything down to an easy speed to probe
sm_config_set_clkdiv(&c, 10.f);
pio_sm_init(inst->pio, inst->sm, inst->prog_offset, &c);
pio_sm_set_enabled(inst->pio, inst->sm, true);
gpio_put(inst->rst_n_pin, 1);
}
// 12-byte command packet to push to the PIO SM. After pushing the packet, can
// either push write data, or wait for read data.
typedef struct {
uint32_t len;
uint32_t cmd0;
uint32_t cmd1_dir_jmp;
} hyperram_cmd_t;
typedef enum {
HRAM_CMD_READ = 0x5,
HRAM_CMD_WRITE = 0x1,
HRAM_CMD_REGWRITE = 0x6
} hyperram_cmd_flags;
// HyperRAM command format from S27KL0641 datasheet:
// ------+------------------------------+------------------------------------+
// Bits | Name | Description |
// ------+------------------------------+------------------------------------+
// 47 | R/W# | 1 for read, 0 for write |
// | | |
// 46 | AS | 0 for memory address space, 1 for |
// | | register space (write only) |
// | | |
// 45 | Burst | 0 for wrapped, 1 for linear |
// | | |
// 44:16 | Row and upper column address | Address bits 31:3, irrelevant bits |
// | | should be 0s |
// | | |
// 15:3 | Reserved | Set to 0 |
// | | |
// 2:0 | Lower column address | Address bits 2:0 |
// ------+------------------------------+------------------------------------+
static inline void _hyperram_cmd_init(hyperram_cmd_t *cmd, hyperram_cmd_flags flags, uint32_t addr, uint len) {
// HyperBus uses halfword addresses, not byte addresses. Additionally we
// require addresses to be word-aligned, to hide the fact we screw up
// endianness.
addr = (addr >> 1) & ~0x1u;
uint32_t addr_l = addr & 0x7u;
uint32_t addr_h = addr >> 3;
// First byte is always 0xff (to set DQs to output), then 24-bit length in same FIFO word.
// Length is number of halfwords, minus one.
cmd->len = (0xffu << 24) | ((len * 2 - 1) & ((1u << 24) - 1));
cmd->cmd0 = (flags << 29) | addr_h;
cmd->cmd1_dir_jmp = addr_l << 16;
}
void __not_in_flash_func(hyperram_read_blocking)(const hyperram_inst_t *inst, uint32_t addr, uint32_t *dst, uint len) {
hyperram_cmd_t cmd;
_hyperram_cmd_init(&cmd, HRAM_CMD_READ, addr, len);
cmd.cmd1_dir_jmp |= inst->prog_offset + hyperram_offset_read;
pio_sm_put_blocking(inst->pio, inst->sm, cmd.len);
pio_sm_put_blocking(inst->pio, inst->sm, cmd.cmd0);
pio_sm_put_blocking(inst->pio, inst->sm, cmd.cmd1_dir_jmp);
for (uint i = 0; i < len; ++i)
dst[i] = pio_sm_get_blocking(inst->pio, inst->sm);
}
void __not_in_flash_func(hyperram_write_blocking)(const hyperram_inst_t *inst, uint32_t addr, const uint32_t *src, uint len) {
hyperram_cmd_t cmd;
_hyperram_cmd_init(&cmd, HRAM_CMD_WRITE, addr, len);
cmd.cmd1_dir_jmp |= (0xffu << 8) | (inst->prog_offset + hyperram_offset_write);
pio_sm_put_blocking(inst->pio, inst->sm, cmd.len);
pio_sm_put_blocking(inst->pio, inst->sm, cmd.cmd0);
pio_sm_put_blocking(inst->pio, inst->sm, cmd.cmd1_dir_jmp);
for (uint i = 0; i < len; ++i)
pio_sm_put_blocking(inst->pio, inst->sm, src[i]);
}
// Note these are *byte* addresses, so are off by a factor of 2 from those given in datasheet
enum {
HRAM_REG_ID0 = 0u << 12 | 0u << 1,
HRAM_REG_ID1 = 0u << 12 | 1u << 1,
HRAM_REG_CFG0 = 1u << 12 | 0u << 1,
HRAM_REG_CFG1 = 1u << 12 | 1u << 1
};
// We are using an awful hack here to reuse the data write loop for sending a
// command packet followed immediately by a halfword of data. No worries about
// efficiency, because generally you only write to the config register once.
void hyperram_cfgreg_write(const hyperram_inst_t *inst, uint16_t wdata) {
hyperram_cmd_t cmd;
_hyperram_cmd_init(&cmd, HRAM_CMD_REGWRITE, HRAM_REG_CFG0, 0);
// Make sure SM has bottomed out on TX empty, because we're about to mess
// with its control flow
uint32_t txstall_mask = 1u << (PIO_FDEBUG_TXSTALL_LSB + inst->sm);
inst->pio->fdebug = txstall_mask;
while (!(inst->pio->fdebug & txstall_mask))
;
// Set DQs to output (note that this uses exec(), so asserts CSn as a result,
// as it doesn't set delay/sideset bits)
pio_sm_set_consecutive_pindirs(inst->pio, inst->sm, inst->dq_base_pin, 8, true);
// Preload Y register for the data loop (number of halfwords - 1)
pio_sm_exec(inst->pio, inst->sm, pio_encode_set(pio_y, 3));
// Note the difference between offset_write and offset_write_loop is whether
// RWDS is asserted first (only true for write)
pio_sm_exec(inst->pio, inst->sm, pio_encode_jmp(
inst->prog_offset + hyperram_offset_write_loop
));
pio_sm_put(inst->pio, inst->sm, cmd.cmd0);
pio_sm_put(inst->pio, inst->sm, cmd.cmd1_dir_jmp | wdata);
}

Wyświetl plik

@ -1,23 +0,0 @@
#ifndef _HYPERRAM_H
#define _HYPERRAM_H
#include "hardware/pio.h"
typedef struct {
PIO pio;
uint sm;
uint prog_offset;
// DQ0->DQ7 starting here:
uint dq_base_pin;
// CSn, RWDS, CK, starting here:
uint ctrl_base_pin;
uint rst_n_pin;
} hyperram_inst_t;
void hyperram_pio_init(const hyperram_inst_t *inst);
void hyperram_read_blocking(const hyperram_inst_t *inst, uint32_t addr, uint32_t *dst, uint len);
void hyperram_write_blocking(const hyperram_inst_t *inst, uint32_t addr, const uint32_t *src, uint len);
void hyperram_cfgreg_write(const hyperram_inst_t *inst, uint16_t wdata);
#endif

Wyświetl plik

@ -1,74 +0,0 @@
.program hyperram
; HyperRAM interface. HyperBus clock is clk_sys/4 at maximum.
; - out/in pins are mapped to DQx
; - set pin is mapped to RWDS
; - sideset pins are mapped to {CK, RWDS, CSn} (RWDS needn't be here except I
; screwed up making the dev boards)
; - If variable-latency support is added, map jmp pin to RWDS
;
; Command format (all little-endian in FIFO words):
; - 1-byte initial DQ direction (always 0xff)
; - 3-byte transfer length (number of halfwords transferred - 1)
; (total transfer must be a multiple of 4 bytes)
; - 6-byte command packet, as per HyperRAM datasheet
; - 1 byte to set DQ directions
; - 1 byte jump vector, to dispatch to read/write routine
;
; For writes, the data follows the command immediately in the TX FIFO. For
; reads, data should be popped from RX FIFO.
.side_set 3
.define LATENCY 9
; /-------- CK
; |/------- RWDS
; ||/------ CSn
start: ; |||
out pindirs, 8 side 0b001 [1] ; Set pins to output. Stall with CSn deasserted
out y, 24 side 0b001 [1] ; Get data count
do_cmd:
set x, 2 side 0b000 [1] ; Always 6 bytes in command packet
cmd_loop:
out pins, 8 side 0b000
nop side 0b100 ; Clock edge equidistant between data transitions
out pins, 8 side 0b100
jmp x-- cmd_loop side 0b000
set x, LATENCY side 0b000
delay_loop:
nop side 0b100 [1]
jmp x-- delay_loop side 0b000 [1]
out pindirs, 8 side 0b100 ; Set pin directions based on 10th byte of cmd packet
out pc, 8 side 0b100 ; Dispatch to read/write routine based on 11th byte
public read:
nop side 0b000 [1]
nop side 0b100 [1] ; RAM asserts first data on this edge
read_loop:
in pins, 8 side 0b000 [1] ; Always capture when the following edge is launched
in pins, 8 side 0b100
jmp y--, read_loop side 0b100
jmp start side 0b000 [1] ; We end up with an extra clock pulse (harmless)
public write:
set pindirs, 1 side 0b000 ; Assert RWDS
public write_loop:
out pins, 8 side 0b000
nop side 0b100
out pins, 8 side 0b100
jmp y--, write_loop side 0b000
set pindirs, 0 side 0b000 ; Deassert RWDS, wrap back to start
; This program ignores the toggling of RWDS during reads, so assumes that the
; RAM is able to provide data continuously. This is liable to break if
; transfers cross page or row boundaries, as the memory is allowed to hold
; RWDS low to insert wait states (exact behaviour is implementation-defined).
; Easiest way to fix this is to use a second state machine to handle the read
; data.
;
; The S27KL0641 datasheet is not forthcoming, but close reading of the vendor
; model shows that it does not use this wait state mechanism (which kind of
; makes sense, because there is no corresponding mechanism to insert wait
; states on write)