dbdma: implement LOAD_QUAD, STORE_QUAD and NOP.

This commit is contained in:
Maxim Poliakovski
2023-09-30 16:23:55 +02:00
parent 67146028bf
commit b408a02ef9
2 changed files with 109 additions and 70 deletions

View File

@@ -50,7 +50,59 @@ uint8_t DMAChannel::interpret_cmd() {
if (this->queue_len)
return this->cur_cmd;
// obtain real pointer to the descriptor of the completed command
this->finish_cmd();
}
fetch_cmd(this->cmd_ptr, &cmd_struct);
this->ch_stat &= ~CH_STAT_WAKE; // clear wake bit (DMA spec, 5.5.3.4)
this->cur_cmd = cmd_struct.cmd_key >> 4;
switch (this->cur_cmd) {
case DBDMA_Cmd::OUTPUT_MORE:
case DBDMA_Cmd::OUTPUT_LAST:
case DBDMA_Cmd::INPUT_MORE:
case DBDMA_Cmd::INPUT_LAST:
if (cmd_struct.cmd_key & 7) {
LOG_F(ERROR, "Key > 0 not implemented");
break;
}
this->queue_data = mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count, &is_writable);
this->queue_len = cmd_struct.req_count;
this->cmd_in_progress = true;
break;
case DBDMA_Cmd::STORE_QUAD:
if ((cmd_struct.cmd_key & 7) != 6)
LOG_F(9, "Invalid key %d in STORE_QUAD", cmd_struct.cmd_key & 7);
this->xfer_quad(&cmd_struct, true);
break;
case DBDMA_Cmd::LOAD_QUAD:
if ((cmd_struct.cmd_key & 7) != 6)
LOG_F(9, "Invalid key %d in LOAD_QUAD", cmd_struct.cmd_key & 7);
this->xfer_quad(&cmd_struct, false);
break;
case DBDMA_Cmd::NOP:
this->finish_cmd();
break;
case DBDMA_Cmd::STOP:
this->ch_stat &= ~CH_STAT_ACTIVE;
this->cmd_in_progress = false;
break;
default:
LOG_F(ERROR, "Unsupported DMA command 0x%X", this->cur_cmd);
this->ch_stat |= CH_STAT_DEAD;
this->ch_stat &= ~CH_STAT_ACTIVE;
}
return this->cur_cmd;
}
void DMAChannel::finish_cmd() {
DMACmd cmd_struct;
bool is_writable, branch_taken = false;
// obtain real pointer to the descriptor of the command to be finished
uint8_t *cmd_desc = mmu_get_dma_mem(this->cmd_ptr, 16, &is_writable);
// get command code
@@ -94,70 +146,44 @@ uint8_t DMAChannel::interpret_cmd() {
this->cmd_ptr += 16;
this->cmd_in_progress = false;
}
}
fetch_cmd(this->cmd_ptr, &cmd_struct);
void DMAChannel::xfer_quad(const DMACmd *cmd_desc, const bool is_store) {
bool is_writable;
uint8_t *src, *dst;
this->ch_stat &= ~CH_STAT_WAKE; // clear wake bit (DMA spec, 5.5.3.4)
this->cur_cmd = cmd_struct.cmd_key >> 4;
switch (this->cur_cmd) {
case DBDMA_Cmd::OUTPUT_MORE:
case DBDMA_Cmd::OUTPUT_LAST:
case DBDMA_Cmd::INPUT_MORE:
case DBDMA_Cmd::INPUT_LAST:
if (cmd_struct.cmd_key & 7) {
LOG_F(ERROR, "Key > 0 not implemented");
break;
}
this->queue_data = mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count, &is_writable);
this->queue_len = cmd_struct.req_count;
this->cmd_in_progress = true;
break;
case DBDMA_Cmd::STORE_QUAD:
if (cmd_struct.cmd_key != 6) {
LOG_F(ERROR, "Illegal key value for STORE_QUAD");
}
else {
if (cmd_struct.req_count & 0x4) {
cmd_struct.req_count = 4;
}
else if (cmd_struct.req_count & 0x2) {
cmd_struct.req_count = 2;
}
else {
cmd_struct.req_count = 1;
}
//mmu_dma_store_quad(cmd_struct.address, cmd_struct.cmd_arg);
this->queue_len = 4;
this->cmd_in_progress = true;
}
break;
case DBDMA_Cmd::LOAD_QUAD:
if (cmd_struct.cmd_key != 6) {
LOG_F(ERROR, "Illegal key value for LOAD_QUAD");
// parse and fix reqCount
uint32_t xfer_size = cmd_desc->req_count & 7;
if (xfer_size & 4) {
xfer_size = 4;
} else if (xfer_size & 2) {
xfer_size = 2;
} else {
//this->queue_data = mmu_dma_load_quad(cmd_struct.address);
this->queue_len = 4;
this->cmd_in_progress = true;
}
break;
case DBDMA_Cmd::NOP:
LOG_F(ERROR, "Unsupported DMA Command NOP");
break;
case DBDMA_Cmd::STOP:
this->ch_stat &= ~CH_STAT_ACTIVE;
this->cmd_in_progress = false;
break;
default:
LOG_F(ERROR, "Unsupported DMA command 0x%X", this->cur_cmd);
this->ch_stat |= CH_STAT_DEAD;
this->ch_stat &= ~CH_STAT_ACTIVE;
xfer_size = 1;
}
return this->cur_cmd;
// prepare data pointers
if (is_store) {
src = mmu_get_dma_mem(this->cmd_ptr, 16, &is_writable);
src += 8; // move src to cmd.data32
dst = mmu_get_dma_mem(cmd_desc->address & ~(xfer_size - 1), xfer_size,
&is_writable);
} else {
src = mmu_get_dma_mem(cmd_desc->address & ~(xfer_size - 1), xfer_size,
&is_writable);
dst = mmu_get_dma_mem(this->cmd_ptr, 16, &is_writable);
dst += 8; // move dst to cmd.data32
}
// perform data transfer only if the destination is writable
if (is_writable) {
std::memcpy(dst, src, xfer_size);
}
if (cmd_desc->cmd_bits & 0xC)
ABORT_F("DBDMA: cmd_bits.b should be zero for LOAD/STORE_QUAD!");
this->finish_cmd();
}
void DMAChannel::update_irq() {
@@ -366,6 +392,17 @@ void DMAChannel::start() {
if (this->start_cb)
this->start_cb();
this->cmd_in_progress = false;
// some DBDMA programs contain commands that don't transfer data
// between a device and memory (LOAD_QUAD, STORE_QUAD, NOP and STOP).
// We thus interprete the DBDMA program until a data transfer between
// a device and memory is queued or the channel becomes idle/dead.
while (!this->cmd_in_progress && !(this->ch_stat & CH_STAT_DEAD) &&
(this->ch_stat & CH_STAT_ACTIVE)) {
this->interpret_cmd();
}
}
void DMAChannel::resume() {

View File

@@ -103,6 +103,8 @@ public:
protected:
void fetch_cmd(uint32_t cmd_addr, DMACmd* p_cmd);
uint8_t interpret_cmd(void);
void finish_cmd();
void xfer_quad(const DMACmd *cmd_desc, const bool is_store);
void update_irq();
void start(void);