kopia lustrzana https://github.com/Wren6991/PicoDVI
Delete unfinished HyperRAM code, it can be resurrected from the history at some point
rodzic
14c27eaeae
commit
6895447bfd
|
@ -22,7 +22,6 @@ include_directories(
|
|||
)
|
||||
|
||||
add_subdirectory(libdvi)
|
||||
add_subdirectory(libhyperram)
|
||||
add_subdirectory(libsprite)
|
||||
|
||||
add_subdirectory(apps)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
add_executable(hyperram_test
|
||||
main.c
|
||||
)
|
||||
|
||||
target_link_libraries(hyperram_test
|
||||
pico_stdlib
|
||||
libhyperram
|
||||
)
|
||||
|
||||
pico_add_extra_outputs(hyperram_test)
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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)
|
Ładowanie…
Reference in New Issue