Created
January 4, 2016 17:40
-
-
Save shuffle2/6cf82e20c9d664f7ee85 to your computer and use it in GitHub Desktop.
Simple decoding for flash using QREAD
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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