Skip to content

Instantly share code, notes, and snippets.

@shuffle2
Created January 4, 2016 17:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shuffle2/6cf82e20c9d664f7ee85 to your computer and use it in GitHub Desktop.
Save shuffle2/6cf82e20c9d664f7ee85 to your computer and use it in GitHub Desktop.
Simple decoding for flash using QREAD
// This plugin is aware of Macronix-specific sflash behavior, such that it may
// fully decode bus transactions.
// latches input on SCLK rising edge, shifts data out on falling edge of SCLK
// CS active low
// supports 4 byte addresses
// supports up to 4-wire parallel IO
#include "../Cmdparser.cpp"
typedef unsigned __int8 u8;
typedef unsigned __int16 u16;
typedef unsigned __int32 u32;
void OnLoad() {}
void OnUnLoad() {}
void SetInitItem(unsigned char ID, unsigned char subID, int value) {}
void EndOfData() {}
enum ConfigNames { CFG_SIO3, CFG_VCC, CFG_RESET, CFG_CS, CFG_SIO1, CFG_SIO2, CFG_SIO0, CFG_SCLK, NUM_CFG };
enum FieldNames { FIELD_DATA, NUM_FIELD };
struct ChannelInfo {
bool invert;
bool disable;
Data64 mask;
};
struct SignalConfig {
ChannelInfo clk;
ChannelInfo cs;
ChannelInfo io[4];
};
static SignalConfig config;
void GetStrList(int id, string_vec &s) {
switch (id) {
case 0:
// plugin description
s.push_back("SPI (Macronix QPI)");
break;
case 1:
// config items
s.assign(NUM_CFG, "");
s[CFG_SIO3] = "DNU/SIO3,chanselect,1,1,1,0,0";
s[CFG_VCC] = "VCC,chanselect,2,1,1,0,0";
s[CFG_RESET] = "RESET#,chanselect,4,1,1,0,0";
s[CFG_CS] = "CS#,chanselect,8,1,1,0,0";
s[CFG_SIO1] = "SO/SIO1,chanselect,0x10,1,1,0,0";
s[CFG_SIO2] = "WP#/SIO2,chanselect,0x20,1,1,0,0";
s[CFG_SIO0] = "SI/SIO0,chanselect,0x40,1,1,0,0";
s[CFG_SCLK] = "SCLK,chanselect,0x80,1,1,0,0";
break;
case 2:
// field formats
s.assign(NUM_FIELD, "");
s[FIELD_DATA] = "MOSI-MISO,,,{:8} : {7:}";
break;
case 3:
// pre-processor name
s.push_back("RAW");
break;
case 4:
// plugin framework version
s.push_back("1");
break;
case 5:
// lookup tables
break;
}
}
void SetCfgItem(unsigned char id, unsigned char sub_id, int value) {
#define SET_CFG(name, member) \
case name: \
switch (sub_id) { \
case 0: \
config.member.invert = !!(value & 1); \
config.member.disable = !!((value >> 1) & 1); \
break; \
case 1: config.member.mask.lowint = value; break; \
case 2: config.member.mask.highint = value; break; \
} \
break;
switch (id) {
//SET_CFG(CFG_VCC, );
//SET_CFG(CFG_RESET, );
SET_CFG(CFG_SCLK, clk);
SET_CFG(CFG_CS, cs);
SET_CFG(CFG_SIO0, io[0]);
SET_CFG(CFG_SIO1, io[1]);
SET_CFG(CFG_SIO2, io[2]);
SET_CFG(CFG_SIO3, io[3]);
}
#undef SET_CFG
}
static bool first_parse;
static bool selected;
static bool clk_prev;
static int64 field_start;
static u8 data_miso, data_mosi;
static int bitcount;
static int seq_bytes;
static int bytes_until_qpi;
void StartOfData() {
first_parse = true;
}
void emit_field(int num_bits) {
bitcount += num_bits;
if (bitcount == 8) {
Data64 data = { 0 };
data.lowint = ((u32)data_mosi << 8) | data_miso;
if (seq_bytes == 0) {
StartFrame(field_start, data, FIELD_DATA);
}
else {
StartField(field_start, data, FIELD_DATA);
}
//EndFrame(timestamp);
if (seq_bytes == 0) {
// first byte - lookup command
if (data_mosi == 0x6b) {
// we always see QREAD with 4byte addr
bytes_until_qpi = 4 + 1;
}
}
else {
bytes_until_qpi--;
}
seq_bytes++;
data_miso = data_mosi = 0;
bitcount = 0;
}
}
void Parse(int64 timestamp, Data64 data) {
// only handles active low for now...
bool cs = !!(data.i64 & config.cs.mask.i64);
bool clk = !!(data.i64 & config.clk.mask.i64);
if (first_parse) {
first_parse = false;
selected = false;
field_start = 0;
data_miso = data_mosi = 0;
bitcount = 0;
seq_bytes = 0;
clk_prev = clk;
}
if (!selected && !cs) {
selected = true;
bitcount = 0;
seq_bytes = 0;
// need to maintain more state...
bytes_until_qpi = INT_MAX;
}
else if (selected && cs) {
selected = false;
Data64 data = { 0 };
data.lowint = ((u32)data_mosi << 8) | data_miso;
//StartFrame(field_start, data, FIELD_DATA);
EndFrame(timestamp);
data_miso = data_mosi = 0;
bitcount = 0;
}
bool clk_rising = !clk_prev && clk;
bool clk_falling = clk_prev && !clk;
if (selected && clk_rising && bitcount == 0) {
field_start = timestamp;
}
bool quad_mode = bytes_until_qpi <= 0;
if (!quad_mode) {
if (selected && clk_rising) {
bool si = !!(data.i64 & config.io[0].mask.i64);
data_mosi <<= 1;
data_mosi |= si ? 1 : 0;
}
else if (selected && clk_falling) {
bool so = !!(data.i64 & config.io[1].mask.i64);
data_miso <<= 1;
data_miso |= so ? 1 : 0;
emit_field(1);
}
}
// QREAD shifts data out on SIO[3:0] on falling edge
// i guess master is intended to latch on/by next rising edge?
else if (selected && clk_rising) {
bool so_bits[4];
for (int i = 0; i < 4; i++) {
so_bits[i] = !!(data.i64 & config.io[i].mask.i64);
}
u8 so = 0;
for (int i = 0; i < 4; i++) {
so |= (so_bits[i] ? 1 : 0) << i;
}
data_miso <<= 4;
data_miso |= so;
emit_field(4);
}
clk_prev = clk;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment