/*!
* @file x393_tasks01.vh
* @date 2015-02-07
* @author Andrey Filippov
*
* @brief Simulation tasks for the x393 (low level)
*
* @copyright Copyright (c) 2015 Elphel, Inc.
*
* License:
*
* x393_tasks01.vh is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* x393_tasks01.vh is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7:
* If you modify this Program, or any covered work, by linking or combining it
* with independent modules provided by the FPGA vendor only (this permission
* does not extend to any 3-rd party modules, "soft cores" or macros) under
* different license terms solely for the purpose of generating binary "bitstream"
* files and/or simulating the code, the copyright holders of this Program give
* you the right to distribute the covered work without those independent modules
* as long as the source code for them is available from the FPGA vendor free of
* charge, and there is no dependence on any encrypted modules for simulating of
* the combined code. This permission applies to you if the distributed code
* contains all the components and scripts required to completely simulate it
* with at least one of the Free Software programs.
*/
// Low-level tasks
// alternative way to check for empty read queue (without a separate counter)
task write_contol_register;
input [29:0] reg_addr;
// input integer reg_addr;
input [31:0] data;
begin
axi_write_single_w(CONTROL_ADDR+reg_addr, data);
end
endtask
task read_contol_register;
input [29:0] reg_addr;
begin
read_and_wait_w(CONTROL_RBACK_ADDR+reg_addr);
end
endtask
task wait_read_queue_empty;
begin
wait (~rvalid && rready && (rid==LAST_ARID)); // nothing left in read queue?
SIMUL_AXI_FULL<=1'b0;
end
endtask
task axi_set_rd_lag;
input [3:0] lag;
begin
@(posedge CLK);
RD_LAG <= lag;
end
endtask
task axi_set_b_lag;
input [3:0] lag;
begin
@(posedge CLK);
B_LAG <= lag;
end
endtask
task read_and_wait_w;
input [29:0] address;
begin
read_and_wait ({address,2'b0});
end
endtask
task read_and_wait;
input [31:0] address;
begin
IRQ_EN = 0;
wait (CLK);
while (!MAIN_GO) begin
wait (!CLK);
wait (CLK);
end
axi_read_addr_inner(
GLOBAL_READ_ID, // id
address & 32'hfffffffc, // addr
4'h0, // len - single
1 // burst type - increment
);
GLOBAL_READ_ID <= GLOBAL_READ_ID+1;
wait (!CLK && rvalid && rready);
wait (CLK);
registered_rdata <= rdata;
wait (!CLK); // registered_rdata should be valid on exit
IRQ_EN = 1;
if (IRQS) begin
@(posedge CLK);
@(negedge CLK);
end
end
endtask
task axi_write_single_w; // address in dwords
input [29:0] address;
input [31:0] data;
begin
`ifdef DEBUG_WR_SINGLE
$display("axi_write_single_w %h:%h @ %t",address,data,$time);
`endif
axi_write_single ({address,2'b0},data);
end
endtask
task axi_write_single; // address in bytes, not words
input [31:0] address;
input [31:0] data;
begin
axi_write_addr_data(
GLOBAL_WRITE_ID, // id
// address << 2, // addr
address & 32'hfffffffc, // addr
data,
4'h0, // len - single
1, // burst type - increment
1'b1, // data_en
4'hf, // wstrb
1'b1 // last
);
GLOBAL_WRITE_ID <= GLOBAL_WRITE_ID+1;
#0.1; // without this delay axi_write_addr_data() used old value of GLOBAL_WRITE_ID
end
endtask
task axi_write_addr_data;
input [11:0] id;
input [31:0] addr;
input [31:0] data;
input [ 3:0] len;
input [ 1:0] burst;
input data_en; // if 0 - do not send data, only address
input [ 3:0] wstrb;
input last;
begin
IRQ_EN = 0;
wait (CLK);
while (!MAIN_GO) begin
wait (!CLK);
wait (CLK);
end
axi_write_addr_data_inner (id, addr, data, len, burst, data_en, wstrb, last);
IRQ_EN = 1;
if (IRQS) begin
@(posedge CLK);
@(negedge CLK);
end
end
endtask
task axi_write_data;
input [11:0] id;
input [31:0] data;
input [ 3:0] wstrb;
input last;
begin
IRQ_EN = 0;
wait (CLK);
while (!MAIN_GO) begin
wait (!CLK);
wait (CLK);
end
axi_write_data_inner (id, data, wstrb, last);
IRQ_EN = 1;
if (IRQS) begin
@(posedge CLK);
@(negedge CLK);
end
end
endtask
// Tasks called from ISR
task read_contol_register_irq;
input [29:0] reg_addr;
output [31:0] rslt;
begin
read_and_wait_w_irq(CONTROL_RBACK_ADDR+reg_addr, rslt);
end
endtask
task read_status_irq;
input [STATUS_DEPTH-1:0] address;
output [31:0] rslt;
begin
read_and_wait_w_irq(STATUS_ADDR + address , rslt);
end
endtask
task read_and_wait_w_irq;
input [29:0] address;
output [31:0] rslt;
begin
read_and_wait_irq ({address,2'b0},rslt);
end
endtask
task read_and_wait_irq;
input [31:0] address;
output reg [31:0] rslt;
begin
axi_read_addr_irq(
GLOBAL_READ_ID, // id
address & 32'hfffffffc, // addr
4'h0, // len - single
1 // burst type - increment
);
GLOBAL_READ_ID <= GLOBAL_READ_ID+1;
wait (!CLK && rvalid && rready);
wait (CLK);
rslt <= rdata;
wait (!CLK); // registered_rdata should be valid on exit
end
endtask
task axi_read_addr_irq; // called ferom the main loop, not from interrupts
input [11:0] id;
input [31:0] addr;
input [ 3:0] len;
input [ 1:0] burst;
begin
// IRQ_EN = 0;
// wait (CLK);
// while (!MAIN_GO) begin
// wait (!CLK);
// wait (CLK);
// end
axi_read_addr_inner (id, addr, len, burst);
// IRQ_EN = 1;
end
endtask
task write_contol_register_irq;
input [29:0] reg_addr;
// input integer reg_addr;
input [31:0] data;
begin
axi_write_single_w_irq(CONTROL_ADDR+reg_addr, data);
end
endtask
task axi_write_single_w_irq; // address in dwords
input [29:0] address;
input [31:0] data;
begin
`ifdef DEBUG_WR_SINGLE
$display("axi_write_single_w %h:%h @ %t",address,data,$time);
`endif
axi_write_single_irq ({address,2'b0},data);
end
endtask
task axi_write_single_irq; // address in bytes, not words
input [31:0] address;
input [31:0] data;
begin
axi_write_addr_data_irq(
GLOBAL_WRITE_ID, // id
address & 32'hfffffffc, // addr
data,
4'h0, // len - single
1, // burst type - increment
1'b1, // data_en
4'hf, // wstrb
1'b1 // last
);
GLOBAL_WRITE_ID <= GLOBAL_WRITE_ID+1;
#0.1; // without this delay axi_write_addr_data() used old value of GLOBAL_WRITE_ID
end
endtask
task axi_write_addr_data_irq;
input [11:0] id;
input [31:0] addr;
input [31:0] data;
input [ 3:0] len;
input [ 1:0] burst;
input data_en; // if 0 - do not send data, only address
input [ 3:0] wstrb;
input last;
begin
// IRQ_EN = 0;
// wait (CLK);
// while (!MAIN_GO) begin
// wait (!CLK);
// wait (CLK);
// end
axi_write_addr_data_inner (id, addr, data, len, burst, data_en, wstrb, last);
// IRQ_EN = 1;
end
endtask
// Tasks common for main ind ISR
task axi_write_addr_data_inner;
input [11:0] id;
input [31:0] addr;
input [31:0] data;
input [ 3:0] len;
input [ 1:0] burst;
input data_en; // if 0 - do not send data, only address
input [ 3:0] wstrb;
input last;
reg data_sent;
// wire data_sent_d;
// assign #(.1) data_sent_d= data_sent;
begin
wait (!CLK && AW_READY);
AWID_IN_r <= id;
AWADDR_IN_r <= addr;
AWLEN_IN_r <= len;
AWSIZE_IN_r <= 2'b10;
AWBURST_IN_r <= burst;
AW_SET_CMD_r <= 1'b1;
if (data_en && W_READY) begin
WID_IN_r <= id;
WDATA_IN_r <= data;
WSTRB_IN_r <= wstrb;
WLAST_IN_r <= last;
W_SET_CMD_r <= 1'b1;
data_sent <= 1'b1;
end else begin
data_sent <= 1'b0;
end
DEBUG1 <=1'b1;
wait (CLK);
DEBUG1 <=1'b0;
AWID_IN_r <= 'hz;
AWADDR_IN_r <= 'hz;
AWLEN_IN_r <= 'hz;
AWSIZE_IN_r <= 'hz;
AWBURST_IN_r <= 'hz;
AW_SET_CMD_r <= 1'b0;
DEBUG2 <=1'b1;
if (data_sent) begin
WID_IN_r <= 'hz;
WDATA_IN_r <= 'hz;
WSTRB_IN_r <= 'hz;
WLAST_IN_r <= 'hz;
W_SET_CMD_r <= 1'b0;
end
// Now sent data if it was not sent simultaneously with the address
if (data_en && !data_sent) begin
DEBUG3 <=1'b1;
wait (!CLK && W_READY);
DEBUG3 <=1'b0;
WID_IN_r <= id;
WDATA_IN_r <= data;
WSTRB_IN_r <= wstrb;
WLAST_IN_r <= last;
W_SET_CMD_r <= 1'b1;
wait (CLK);
DEBUG3 <=1'bx;
WID_IN_r <= 'hz;
WDATA_IN_r <= 'hz;
WSTRB_IN_r <= 'hz;
WLAST_IN_r <= 'hz;
W_SET_CMD_r <= 1'b0;
end
DEBUG2 <=1'b0;
#0.1;
data_sent <= 1'b0;
#0.1;
end
endtask
task axi_write_data_inner;
input [11:0] id;
input [31:0] data;
input [ 3:0] wstrb;
input last;
begin
wait (!CLK && W_READY);
WID_IN_r <= id;
WDATA_IN_r <= data;
WSTRB_IN_r <= wstrb;
WLAST_IN_r <= last;
W_SET_CMD_r <= 1'b1;
wait (CLK);
WID_IN_r <= 12'hz;
WDATA_IN_r <= 'hz;
WSTRB_IN_r <= 4'hz;
WLAST_IN_r <= 1'bz;
W_SET_CMD_r <= 1'b0;
#0.1;
end
endtask
task axi_read_addr_inner; // regardless of main loop/interrupts
input [11:0] id;
input [31:0] addr;
input [ 3:0] len;
input [ 1:0] burst;
begin
wait (!CLK && AR_READY);
ARID_IN_r <= id;
ARADDR_IN_r <= addr;
ARLEN_IN_r <= len;
ARSIZE_IN_r <= 2'b10;
ARBURST_IN_r <= burst;
AR_SET_CMD_r <= 1'b1;
wait (CLK);
ARID_IN_r <= 12'hz;
ARADDR_IN_r <= 'hz;
ARLEN_IN_r <= 4'hz;
ARSIZE_IN_r <= 2'hz;
ARBURST_IN_r <= 2'hz;
AR_SET_CMD_r <= 1'b0;
LAST_ARID <= id;
NUM_WORDS_EXPECTED <= NUM_WORDS_EXPECTED+len+1;
end
endtask