Added timer, still wip
This commit is contained in:
108
project.cfg
108
project.cfg
@@ -22,8 +22,8 @@ package = tqg144
|
||||
speedgrade = -2
|
||||
toplevel = top_generic
|
||||
xst_opts = -vlgincdir rtl/util
|
||||
files_verilog = rtl/util/conv.vh
|
||||
rtl/toplevel/top_generic.v
|
||||
files_verilog = rtl/toplevel/top_generic.v
|
||||
rtl/util/conv.vh
|
||||
rtl/core/nco_q15.v
|
||||
rtl/core/sigmadelta_sampler.v
|
||||
rtl/core/sigmadelta_rcmodel_q15.v
|
||||
@@ -31,8 +31,14 @@ files_verilog = rtl/util/conv.vh
|
||||
rtl/core/mul_const.v
|
||||
rtl/core/lpf_iir_q15_k.v
|
||||
rtl/core/decimate_by_r_q15.v
|
||||
rtl/core/mcu_peripherals.v
|
||||
rtl/core/mcu.v
|
||||
rtl/core/mem_jtag_writable.v
|
||||
# Arch
|
||||
rtl/arch/spartan-6/lvds_comparator.v
|
||||
rtl/arch/spartan-6/clk_gen.v
|
||||
rtl/arch/spartan-6/jtag_if.v
|
||||
# SERV
|
||||
rtl/serv/serv_aligner.v
|
||||
rtl/serv/serv_alu.v
|
||||
rtl/serv/serv_bufreg.v
|
||||
@@ -47,25 +53,104 @@ files_verilog = rtl/util/conv.vh
|
||||
rtl/serv/serv_rf_if.v
|
||||
rtl/serv/serv_rf_ram_if.v
|
||||
rtl/serv/serv_rf_ram.v
|
||||
rtl/serv/serv_rf_top.v
|
||||
rtl/serv/serv_state.v
|
||||
rtl/serv/serv_rf_top.v
|
||||
rtl/serv/serv_synth_wrapper.v
|
||||
rtl/serv/serv_top.v
|
||||
# QERV
|
||||
# rtl/qerv/serv_rf_top.v
|
||||
# rtl/qerv/serv_synth_wrapper.v
|
||||
# rtl/qerv/serv_top.v
|
||||
# rtl/qerv/qerv_immdec.v
|
||||
# Servile
|
||||
rtl/serv/servile_arbiter.v
|
||||
rtl/serv/servile_mux.v
|
||||
rtl/serv/servile_rf_mem_if.v
|
||||
rtl/serv/servile.v
|
||||
rtl/serv/serving_ram.v
|
||||
rtl/serv/serving.v
|
||||
rtl/arch/spartan-6/jtag_if.v
|
||||
# rtl/qerv/servile_arbiter.v
|
||||
# rtl/qerv/servile_mux.v
|
||||
# rtl/qerv/servile_rf_mem_if.v
|
||||
# rtl/qerv/servile.v
|
||||
# WB
|
||||
rtl/wb/wb_gpio.v
|
||||
rtl/wb/wb_gpio_banks.v
|
||||
rtl/wb/wb_mux.v
|
||||
rtl/wb/jtag_wb_bridge.v
|
||||
rtl/core/mcu.v
|
||||
rtl/wb/wb_timer.v
|
||||
|
||||
files_con = boards/mimas_v1/constraints.ucf
|
||||
files_other = rtl/util/rc_alpha_q15.vh
|
||||
rtl/util/clog2.vh
|
||||
sw/blinky/blinky.hex
|
||||
sw/sweep/sweep.hex
|
||||
|
||||
[target.synth_sim]
|
||||
toolchain = iverilog
|
||||
runtime = all
|
||||
toplevel = tb_top_generic
|
||||
ivl_opts = -Irtl/util
|
||||
files_verilog = rtl/toplevel/top_generic.v
|
||||
rtl/util/conv.vh
|
||||
rtl/core/nco_q15.v
|
||||
rtl/core/sigmadelta_sampler.v
|
||||
rtl/core/sigmadelta_rcmodel_q15.v
|
||||
rtl/core/sigmadelta_input_q15.v
|
||||
rtl/core/mul_const.v
|
||||
rtl/core/lpf_iir_q15_k.v
|
||||
rtl/core/decimate_by_r_q15.v
|
||||
rtl/core/mcu_peripherals.v
|
||||
rtl/core/mcu.v
|
||||
rtl/core/mem_jtag_writable.v
|
||||
# Arch
|
||||
rtl/core/lvds_comparator.v
|
||||
sim/overrides/clk_gen.v
|
||||
rtl/core/jtag_if.v
|
||||
# SERV
|
||||
rtl/serv/serv_aligner.v
|
||||
rtl/serv/serv_alu.v
|
||||
rtl/serv/serv_bufreg.v
|
||||
rtl/serv/serv_bufreg2.v
|
||||
rtl/serv/serv_compdec.v
|
||||
rtl/serv/serv_csr.v
|
||||
rtl/serv/serv_ctrl.v
|
||||
rtl/serv/serv_debug.v
|
||||
rtl/serv/serv_decode.v
|
||||
rtl/serv/serv_immdec.v
|
||||
rtl/serv/serv_mem_if.v
|
||||
rtl/serv/serv_rf_if.v
|
||||
rtl/serv/serv_rf_ram_if.v
|
||||
rtl/serv/serv_rf_ram.v
|
||||
rtl/serv/serv_state.v
|
||||
rtl/serv/serv_rf_top.v
|
||||
rtl/serv/serv_synth_wrapper.v
|
||||
rtl/serv/serv_top.v
|
||||
# QERV
|
||||
# rtl/qerv/serv_rf_top.v
|
||||
# rtl/qerv/serv_synth_wrapper.v
|
||||
# rtl/qerv/serv_top.v
|
||||
# rtl/qerv/qerv_immdec.v
|
||||
# Servile
|
||||
rtl/serv/servile_arbiter.v
|
||||
rtl/serv/servile_mux.v
|
||||
rtl/serv/servile_rf_mem_if.v
|
||||
rtl/serv/servile.v
|
||||
# rtl/qerv/servile_arbiter.v
|
||||
# rtl/qerv/servile_mux.v
|
||||
# rtl/qerv/servile_rf_mem_if.v
|
||||
# rtl/qerv/servile.v
|
||||
# WB
|
||||
rtl/wb/wb_gpio.v
|
||||
rtl/wb/wb_gpio_banks.v
|
||||
rtl/wb/wb_mux.v
|
||||
rtl/wb/jtag_wb_bridge.v
|
||||
rtl/wb/wb_timer.v
|
||||
|
||||
sim/tb/tb_top_generic.v
|
||||
|
||||
files_con = boards/mimas_v1/constraints.ucf
|
||||
files_other = rtl/util/rc_alpha_q15.vh
|
||||
rtl/util/clog2.vh
|
||||
rtl/util/conv.vh
|
||||
sw/sweep/sweep.hex
|
||||
|
||||
[target.jtag]
|
||||
toolchain = ISE
|
||||
@@ -93,6 +178,13 @@ files_verilog = sim/tb/tb_svf.v
|
||||
rtl/core/cdc_strobed.v
|
||||
files_other = sim/other/test.svf
|
||||
|
||||
[target.tb_wb_timer]
|
||||
toolchain = iverilog
|
||||
runtime = all
|
||||
toplevel = tb_wb_timer
|
||||
files_verilog = sim/tb/tb_wb_timer.v
|
||||
rtl/wb/wb_timer.v
|
||||
|
||||
[target.tools]
|
||||
toolchain = make
|
||||
output_files = tools/test
|
||||
|
||||
120
rtl/core/mcu.v
120
rtl/core/mcu.v
@@ -16,7 +16,8 @@ module mcu #(
|
||||
output wire [31:0] o_GPO_A,
|
||||
output wire [31:0] o_GPO_B,
|
||||
output wire [31:0] o_GPO_C,
|
||||
output wire [31:0] o_GPO_D
|
||||
output wire [31:0] o_GPO_D,
|
||||
output wire o_test
|
||||
);
|
||||
localparam WITH_CSR = 1;
|
||||
localparam regs = 32+WITH_CSR*4;
|
||||
@@ -26,7 +27,8 @@ module mcu #(
|
||||
wire rst_mem_reason;
|
||||
wire timer_irq;
|
||||
assign rst = i_rst | rst_mem_reason;
|
||||
assign timer_irq = 1'b0;
|
||||
|
||||
assign o_test = timer_irq;
|
||||
|
||||
// Busses
|
||||
// CPU->memory
|
||||
@@ -72,9 +74,6 @@ module mcu #(
|
||||
assign GPI[32*3-1:32*2] = i_GPI_C;
|
||||
assign GPI[32*4-1:32*3] = i_GPI_D;
|
||||
|
||||
assign wb_ext_ack = wb_ext_gpio_ack;
|
||||
assign wb_ext_rdt = wb_ext_gpio_rdt;
|
||||
|
||||
// SERV core with mux splitting dbus into mem and ext and
|
||||
// arbiter combining mem and ibus
|
||||
// separate rst line to let other hardware keep core under reset
|
||||
@@ -89,7 +88,7 @@ module mcu #(
|
||||
) servile (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(rst),
|
||||
.i_timer_irq(timer_irq),
|
||||
.i_timer_irq(1'b0), //timer_irq),
|
||||
|
||||
//Memory interface
|
||||
.o_wb_mem_adr(wb_mem_adr),
|
||||
@@ -125,7 +124,7 @@ module mcu #(
|
||||
.rf_regs(regs)
|
||||
) rf_mem_if (
|
||||
.i_clk (i_clk),
|
||||
.i_rst (i_rst),
|
||||
.i_rst (rst),
|
||||
|
||||
.i_waddr(rf_waddr),
|
||||
.i_wdata(rf_wdata),
|
||||
@@ -165,111 +164,20 @@ module mcu #(
|
||||
.o_core_reset(rst_mem_reason)
|
||||
);
|
||||
|
||||
wb_gpio_banks #(
|
||||
.BASE_ADDR(32'h40000000),
|
||||
.NUM_BANKS(4)
|
||||
) gpio (
|
||||
.i_wb_clk(i_clk),
|
||||
.i_wb_rst(rst),
|
||||
.i_wb_dat(wb_ext_dat),
|
||||
mcu_peripherals peripherals (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(rst),
|
||||
.i_wb_adr(wb_ext_adr),
|
||||
.i_wb_dat(wb_ext_dat),
|
||||
.i_wb_sel(wb_ext_sel),
|
||||
.i_wb_we(wb_ext_we),
|
||||
.i_wb_stb(wb_ext_stb),
|
||||
.i_wb_sel(wb_ext_sel),
|
||||
.o_wb_rdt(wb_ext_rdt),
|
||||
.o_wb_ack(wb_ext_ack),
|
||||
// Peripheral IO
|
||||
.i_gpio(GPI),
|
||||
.o_gpio(GPO)
|
||||
.o_gpio(GPO),
|
||||
.o_timer_irq(timer_irq)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
module memory #(
|
||||
parameter memfile = "",
|
||||
parameter depth = 256,
|
||||
parameter sim = 1'b0,
|
||||
localparam aw = `CLOG2(depth)
|
||||
)(
|
||||
input wire i_clk,
|
||||
input wire i_rst,
|
||||
input wire [aw-1:0] i_waddr,
|
||||
input wire [7:0] i_wdata,
|
||||
input wire i_wen,
|
||||
input wire [aw-1:0] i_raddr,
|
||||
output reg [7:0] o_rdata,
|
||||
output wire o_core_reset
|
||||
);
|
||||
// The actual memory
|
||||
reg [7:0] mem [0:depth-1];
|
||||
wire [aw-1:0] mem_adr;
|
||||
assign mem_adr = (i_wen==1'b1) ? i_waddr :
|
||||
i_raddr;
|
||||
|
||||
// Second port wishbone
|
||||
wire [31:0] wb_adr;
|
||||
wire [31:0] wb_dat;
|
||||
reg [31:0] wb_rdt;
|
||||
wire [3:0] wb_sel;
|
||||
wire wb_cyc;
|
||||
wire wb_we;
|
||||
wire wb_stb;
|
||||
reg wb_ack;
|
||||
reg wb_req_d;
|
||||
wire cmd_reset;
|
||||
// Driven by JTAG
|
||||
jtag_wb_bridge #(
|
||||
.chain(1),
|
||||
.byte_aligned(1)
|
||||
) jtag_wb (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(i_rst),
|
||||
|
||||
.o_wb_adr(wb_adr),
|
||||
.o_wb_dat(wb_dat),
|
||||
.o_wb_sel(wb_sel),
|
||||
.o_wb_we(wb_we),
|
||||
.o_wb_cyc(wb_cyc),
|
||||
.o_wb_stb(wb_stb),
|
||||
.i_wb_rdt(wb_rdt),
|
||||
.i_wb_ack(wb_ack),
|
||||
.o_cmd_reset(cmd_reset)
|
||||
);
|
||||
|
||||
assign o_core_reset = cmd_reset;
|
||||
|
||||
// Read/Write
|
||||
always @(posedge i_clk) begin
|
||||
if (i_rst) begin
|
||||
wb_req_d <= 1'b0;
|
||||
wb_ack <= 1'b0;
|
||||
wb_rdt <= 32'h00000000;
|
||||
o_rdata <= 32'h00000000;
|
||||
end else begin
|
||||
if (i_wen)
|
||||
mem[mem_adr] <= i_wdata;
|
||||
o_rdata <= mem[mem_adr];
|
||||
|
||||
wb_req_d <= wb_stb && wb_cyc;
|
||||
wb_ack <= wb_req_d;
|
||||
if (wb_we && wb_stb && wb_cyc)
|
||||
mem[wb_adr[aw-1:0]] <= wb_dat[7:0];
|
||||
wb_rdt <= {24'h000000, mem[wb_adr[aw-1:0]]};
|
||||
end
|
||||
end
|
||||
|
||||
// Preload memory
|
||||
integer i;
|
||||
initial begin
|
||||
if(sim==1'b1) begin
|
||||
for (i = 0; i < depth; i = i + 1)
|
||||
mem[i] = 8'h00;
|
||||
end
|
||||
if(|memfile) begin
|
||||
$display("Preloading %m from %s", memfile);
|
||||
$readmemh(memfile, mem);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
endmodule
|
||||
|
||||
123
rtl/core/mcu_peripherals.v
Normal file
123
rtl/core/mcu_peripherals.v
Normal file
@@ -0,0 +1,123 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module mcu_peripherals (
|
||||
input wire i_clk,
|
||||
input wire i_rst,
|
||||
|
||||
input wire [31:0] i_wb_adr,
|
||||
input wire [31:0] i_wb_dat,
|
||||
input wire [3:0] i_wb_sel,
|
||||
input wire i_wb_we,
|
||||
input wire i_wb_stb,
|
||||
output wire [31:0] o_wb_rdt,
|
||||
output wire o_wb_ack,
|
||||
|
||||
input wire [4*32-1:0] i_gpio,
|
||||
output wire [4*32-1:0] o_gpio,
|
||||
output wire o_timer_irq
|
||||
);
|
||||
localparam [31:0] GPIO_BASE_ADDR = 32'h4000_0000;
|
||||
localparam [31:0] GPIO_ADDR_MASK = 32'hFFFF_0000;
|
||||
localparam [31:0] TIMER_BASE_ADDR = 32'h4001_0000;
|
||||
localparam [31:0] TIMER_ADDR_MASK = 32'hFFFF_0000;
|
||||
|
||||
wire [2*32-1:0] wbs_adr;
|
||||
wire [2*32-1:0] wbs_dat_w;
|
||||
wire [2*4-1:0] wbs_sel;
|
||||
wire [1:0] wbs_we;
|
||||
wire [1:0] wbs_cyc;
|
||||
wire [1:0] wbs_stb;
|
||||
wire [2*3-1:0] wbs_cti;
|
||||
wire [2*2-1:0] wbs_bte;
|
||||
wire [2*32-1:0] wbs_dat_r;
|
||||
wire [1:0] wbs_ack;
|
||||
|
||||
wire [31:0] gpio_wbs_adr = wbs_adr[0*32 +: 32];
|
||||
wire [31:0] gpio_wbs_dat_w = wbs_dat_w[0*32 +: 32];
|
||||
wire [3:0] gpio_wbs_sel = wbs_sel[0*4 +: 4];
|
||||
wire gpio_wbs_we = wbs_we[0];
|
||||
wire gpio_wbs_cyc = wbs_cyc[0];
|
||||
wire gpio_wbs_stb = wbs_stb[0];
|
||||
wire [31:0] gpio_wbs_dat_r;
|
||||
wire gpio_wbs_ack;
|
||||
|
||||
wire [31:0] timer_wbs_dat_w = wbs_dat_w[1*32 +: 32];
|
||||
wire timer_wbs_we = wbs_we[1];
|
||||
wire timer_wbs_cyc = wbs_cyc[1];
|
||||
wire timer_wbs_stb = wbs_stb[1];
|
||||
wire [31:0] timer_wbs_dat_r;
|
||||
wire timer_wbs_ack;
|
||||
|
||||
wb_mux #(
|
||||
.dw(32),
|
||||
.aw(32),
|
||||
.num_slaves(2),
|
||||
.MATCH_ADDR({TIMER_BASE_ADDR, GPIO_BASE_ADDR}),
|
||||
.MATCH_MASK({TIMER_ADDR_MASK, GPIO_ADDR_MASK})
|
||||
) ext_mux (
|
||||
.wb_clk_i(i_clk),
|
||||
.wb_rst_i(i_rst),
|
||||
|
||||
.wbm_adr_i(i_wb_adr),
|
||||
.wbm_dat_i(i_wb_dat),
|
||||
.wbm_sel_i(i_wb_sel),
|
||||
.wbm_we_i(i_wb_we),
|
||||
.wbm_cyc_i(i_wb_stb),
|
||||
.wbm_stb_i(i_wb_stb),
|
||||
.wbm_cti_i(3'b000),
|
||||
.wbm_bte_i(2'b00),
|
||||
.wbm_dat_o(o_wb_rdt),
|
||||
.wbm_ack_o(o_wb_ack),
|
||||
.wbm_err_o(),
|
||||
.wbm_rty_o(),
|
||||
|
||||
.wbs_adr_o(wbs_adr),
|
||||
.wbs_dat_o(wbs_dat_w),
|
||||
.wbs_sel_o(wbs_sel),
|
||||
.wbs_we_o(wbs_we),
|
||||
.wbs_cyc_o(wbs_cyc),
|
||||
.wbs_stb_o(wbs_stb),
|
||||
.wbs_cti_o(wbs_cti),
|
||||
.wbs_bte_o(wbs_bte),
|
||||
.wbs_dat_i(wbs_dat_r),
|
||||
.wbs_ack_i(wbs_ack),
|
||||
.wbs_err_i(2'b00),
|
||||
.wbs_rty_i(2'b00)
|
||||
);
|
||||
|
||||
wb_gpio_banks #(
|
||||
.BASE_ADDR(GPIO_BASE_ADDR),
|
||||
.NUM_BANKS(4)
|
||||
) gpio (
|
||||
.i_wb_clk(i_clk),
|
||||
.i_wb_rst(i_rst),
|
||||
.i_wb_dat(gpio_wbs_dat_w),
|
||||
.i_wb_adr(gpio_wbs_adr),
|
||||
.i_wb_we(gpio_wbs_we),
|
||||
.i_wb_stb(gpio_wbs_stb & gpio_wbs_cyc),
|
||||
.i_wb_sel(gpio_wbs_sel),
|
||||
.o_wb_rdt(gpio_wbs_dat_r),
|
||||
.o_wb_ack(gpio_wbs_ack),
|
||||
.i_gpio(i_gpio),
|
||||
.o_gpio(o_gpio)
|
||||
);
|
||||
|
||||
assign wbs_dat_r[0*32 +: 32] = gpio_wbs_dat_r;
|
||||
assign wbs_ack[0] = gpio_wbs_ack;
|
||||
|
||||
wb_countdown_timer timer (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(i_rst),
|
||||
.o_irq(o_timer_irq),
|
||||
.i_wb_dat(timer_wbs_dat_w),
|
||||
.o_wb_dat(timer_wbs_dat_r),
|
||||
.i_wb_we(timer_wbs_we),
|
||||
.i_wb_cyc(timer_wbs_cyc),
|
||||
.i_wb_stb(timer_wbs_stb),
|
||||
.o_wb_ack(timer_wbs_ack)
|
||||
);
|
||||
|
||||
|
||||
assign wbs_dat_r[1*32 +: 32] = timer_wbs_dat_r;
|
||||
assign wbs_ack[1] = timer_wbs_ack;
|
||||
endmodule
|
||||
91
rtl/core/mem_jtag_writable.v
Normal file
91
rtl/core/mem_jtag_writable.v
Normal file
@@ -0,0 +1,91 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module memory #(
|
||||
parameter memfile = "",
|
||||
parameter depth = 256,
|
||||
parameter sim = 1'b0,
|
||||
parameter aw = `CLOG2(depth)
|
||||
)(
|
||||
input wire i_clk,
|
||||
input wire i_rst,
|
||||
input wire [aw-1:0] i_waddr,
|
||||
input wire [7:0] i_wdata,
|
||||
input wire i_wen,
|
||||
input wire [aw-1:0] i_raddr,
|
||||
output reg [7:0] o_rdata,
|
||||
output wire o_core_reset
|
||||
);
|
||||
// The actual memory
|
||||
reg [7:0] mem [0:depth-1];
|
||||
wire [aw-1:0] mem_adr;
|
||||
assign mem_adr = (i_wen==1'b1) ? i_waddr :
|
||||
i_raddr;
|
||||
|
||||
// Second port wishbone
|
||||
wire [31:0] wb_adr;
|
||||
wire [31:0] wb_dat;
|
||||
reg [31:0] wb_rdt;
|
||||
wire [3:0] wb_sel;
|
||||
wire wb_cyc;
|
||||
wire wb_we;
|
||||
wire wb_stb;
|
||||
reg wb_ack;
|
||||
reg wb_req_d;
|
||||
wire cmd_reset;
|
||||
// Driven by JTAG
|
||||
jtag_wb_bridge #(
|
||||
.chain(1),
|
||||
.byte_aligned(1)
|
||||
) jtag_wb (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(i_rst),
|
||||
|
||||
.o_wb_adr(wb_adr),
|
||||
.o_wb_dat(wb_dat),
|
||||
.o_wb_sel(wb_sel),
|
||||
.o_wb_we(wb_we),
|
||||
.o_wb_cyc(wb_cyc),
|
||||
.o_wb_stb(wb_stb),
|
||||
.i_wb_rdt(wb_rdt),
|
||||
.i_wb_ack(wb_ack),
|
||||
.o_cmd_reset(cmd_reset)
|
||||
);
|
||||
|
||||
assign o_core_reset = cmd_reset;
|
||||
|
||||
// Read/Write
|
||||
always @(posedge i_clk) begin
|
||||
if (i_rst) begin
|
||||
wb_req_d <= 1'b0;
|
||||
wb_ack <= 1'b0;
|
||||
wb_rdt <= 32'h00000000;
|
||||
o_rdata <= 32'h00000000;
|
||||
end else begin
|
||||
if (i_wen)
|
||||
mem[mem_adr] <= i_wdata;
|
||||
o_rdata <= mem[mem_adr];
|
||||
|
||||
wb_req_d <= wb_stb && wb_cyc;
|
||||
wb_ack <= wb_req_d;
|
||||
if (wb_we && wb_stb && wb_cyc)
|
||||
mem[wb_adr[aw-1:0]] <= wb_dat[7:0];
|
||||
wb_rdt <= {24'h000000, mem[wb_adr[aw-1:0]]};
|
||||
end
|
||||
end
|
||||
|
||||
// Preload memory
|
||||
integer i;
|
||||
initial begin
|
||||
if(sim==1'b1) begin
|
||||
for (i = 0; i < depth; i = i + 1)
|
||||
mem[i] = 8'h00;
|
||||
end
|
||||
if(|memfile) begin
|
||||
$display("Preloading %m from %s", memfile);
|
||||
$readmemh(memfile, mem);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
endmodule
|
||||
@@ -1,77 +0,0 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module soclet #(
|
||||
parameter memfile = "",
|
||||
parameter memsize = 8192,
|
||||
parameter sim = 1'b0
|
||||
)(
|
||||
input wire i_clk,
|
||||
input wire i_rst,
|
||||
|
||||
input wire [31:0] i_GPI_A,
|
||||
input wire [31:0] i_GPI_B,
|
||||
input wire [31:0] i_GPI_C,
|
||||
input wire [31:0] i_GPI_D,
|
||||
|
||||
output wire [31:0] o_GPO_A,
|
||||
output wire [31:0] o_GPO_B,
|
||||
output wire [31:0] o_GPO_C,
|
||||
output wire [31:0] o_GPO_D
|
||||
);
|
||||
|
||||
wire [31:0] wb_adr;
|
||||
wire [31:0] wb_dat;
|
||||
wire [31:0] wb_rdt;
|
||||
wire [3:0] wb_sel;
|
||||
wire wb_we;
|
||||
wire wb_stb;
|
||||
wire wb_ack;
|
||||
|
||||
wire [4*32-1:0] GPO;
|
||||
wire [4*32-1:0] GPI;
|
||||
assign o_GPO_A = GPO[32*1-1:32*0];
|
||||
assign o_GPO_B = GPO[32*2-1:32*1];
|
||||
assign o_GPO_C = GPO[32*3-1:32*2];
|
||||
assign o_GPO_D = GPO[32*4-1:32*3];
|
||||
assign GPI[32*1-1:32*0] = i_GPI_A;
|
||||
assign GPI[32*2-1:32*1] = i_GPI_B;
|
||||
assign GPI[32*3-1:32*2] = i_GPI_C;
|
||||
assign GPI[32*4-1:32*3] = i_GPI_D;
|
||||
|
||||
serving #(
|
||||
.memfile(memfile),
|
||||
.memsize(memsize),
|
||||
.sim(sim),
|
||||
.RESET_STRATEGY("MINI"),
|
||||
.WITH_CSR(1)
|
||||
) serv (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(i_rst),
|
||||
.i_timer_irq(1'b0),
|
||||
.i_wb_rdt(wb_rdt),
|
||||
.i_wb_ack(wb_ack),
|
||||
.o_wb_adr(wb_adr),
|
||||
.o_wb_dat(wb_dat),
|
||||
.o_wb_sel(wb_sel),
|
||||
.o_wb_we(wb_we),
|
||||
.o_wb_stb(wb_stb)
|
||||
);
|
||||
|
||||
wb_gpio_banks #(
|
||||
.BASE_ADDR(32'h40000000),
|
||||
.NUM_BANKS(4)
|
||||
) gpio (
|
||||
.i_wb_clk(i_clk),
|
||||
.i_wb_rst(i_rst),
|
||||
.i_wb_dat(wb_dat),
|
||||
.i_wb_adr(wb_adr),
|
||||
.i_wb_we(wb_we),
|
||||
.i_wb_stb(wb_stb),
|
||||
.i_wb_sel(wb_sel),
|
||||
.i_gpio(GPI),
|
||||
.o_wb_rdt(wb_rdt),
|
||||
.o_wb_ack(wb_ack),
|
||||
.o_gpio(GPO)
|
||||
);
|
||||
|
||||
endmodule
|
||||
@@ -1,6 +1,8 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module top_generic(
|
||||
module top_generic #(
|
||||
parameter sim = 0
|
||||
)(
|
||||
input wire aclk,
|
||||
input wire aresetn,
|
||||
|
||||
@@ -11,9 +13,6 @@ module top_generic(
|
||||
output wire[7:0] LED
|
||||
);
|
||||
`include "conv.vh"
|
||||
assign led_green = 1'b0;
|
||||
assign led_red = 1'b0;
|
||||
assign LED = 8'h00;
|
||||
|
||||
// Clocking
|
||||
wire clk_100;
|
||||
@@ -29,8 +28,11 @@ module top_generic(
|
||||
wire [31:0] GPIO_C;
|
||||
wire [31:0] GPIO_D;
|
||||
|
||||
wire test;
|
||||
|
||||
mcu #(
|
||||
.memfile("../sw/sweep/sweep.hex")
|
||||
.memfile("../sw/sweep/sweep.hex"),
|
||||
.sim(sim)
|
||||
) mcu (
|
||||
.i_clk(clk_15),
|
||||
.i_rst(!aresetn),
|
||||
@@ -41,7 +43,8 @@ module top_generic(
|
||||
.o_GPO_A(GPIO_A),
|
||||
.o_GPO_B(GPIO_B),
|
||||
.o_GPO_C(GPIO_C),
|
||||
.o_GPO_D(GPIO_D)
|
||||
.o_GPO_D(GPIO_D),
|
||||
.o_test(test)
|
||||
);
|
||||
|
||||
|
||||
@@ -64,4 +67,8 @@ module top_generic(
|
||||
dac_code <= q15_to_uq16(sin_q15) >> 10;
|
||||
end
|
||||
assign r2r = dac_code;
|
||||
|
||||
assign LED = GPIO_B[7:0];
|
||||
assign led_green = GPIO_C[0];
|
||||
assign led_red = test;
|
||||
endmodule
|
||||
|
||||
145
rtl/wb/wb_mux.v
Normal file
145
rtl/wb/wb_mux.v
Normal file
@@ -0,0 +1,145 @@
|
||||
/* wb_mux. Part of wb_intercon
|
||||
*
|
||||
* ISC License
|
||||
*
|
||||
* Copyright (C) 2013-2019 Olof Kindgren <olof.kindgren@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
Wishbone multiplexer, burst-compatible
|
||||
|
||||
Simple mux with an arbitrary number of slaves.
|
||||
|
||||
The parameters MATCH_ADDR and MATCH_MASK are flattened arrays
|
||||
aw*NUM_SLAVES sized arrays that are used to calculate the
|
||||
active slave. slave i is selected when
|
||||
(wb_adr_i & MATCH_MASK[(i+1)*aw-1:i*aw] is equal to
|
||||
MATCH_ADDR[(i+1)*aw-1:i*aw]
|
||||
If several regions are overlapping, the slave with the lowest
|
||||
index is selected. This can be used to have fallback
|
||||
functionality in the last slave, in case no other slave was
|
||||
selected.
|
||||
|
||||
If no match is found, the wishbone transaction will stall and
|
||||
an external watchdog is required to abort the transaction
|
||||
|
||||
Todo:
|
||||
Registered master/slave connections
|
||||
Rewrite with System Verilog 2D arrays when tools support them
|
||||
*/
|
||||
`include "../util/clog2.vh"
|
||||
|
||||
module wb_mux
|
||||
#(parameter dw = 32, // Data width
|
||||
parameter aw = 32, // Address width
|
||||
parameter num_devices = 2, // Number of devices
|
||||
parameter num_slaves = num_devices, // Number of devices (deprecated)
|
||||
parameter [num_slaves*aw-1:0] MATCH_ADDR = 0,
|
||||
parameter [num_slaves*aw-1:0] MATCH_MASK = 0)
|
||||
|
||||
(
|
||||
input wire wb_clk_i,
|
||||
input wire wb_rst_i,
|
||||
|
||||
// Master Interface
|
||||
input wire [aw-1:0] wbm_adr_i,
|
||||
input wire [dw-1:0] wbm_dat_i,
|
||||
input wire [3:0] wbm_sel_i,
|
||||
input wire wbm_we_i,
|
||||
input wire wbm_cyc_i,
|
||||
input wire wbm_stb_i,
|
||||
input wire [2:0] wbm_cti_i,
|
||||
input wire [1:0] wbm_bte_i,
|
||||
output wire [dw-1:0] wbm_dat_o,
|
||||
output wire wbm_ack_o,
|
||||
output wire wbm_err_o,
|
||||
output wire wbm_rty_o,
|
||||
// Wishbone Slave interface
|
||||
output wire [num_slaves*aw-1:0] wbs_adr_o,
|
||||
output wire [num_slaves*dw-1:0] wbs_dat_o,
|
||||
output wire [num_slaves*4-1:0] wbs_sel_o,
|
||||
output wire [num_slaves-1:0] wbs_we_o,
|
||||
output wire [num_slaves-1:0] wbs_cyc_o,
|
||||
output wire [num_slaves-1:0] wbs_stb_o,
|
||||
output wire [num_slaves*3-1:0] wbs_cti_o,
|
||||
output wire [num_slaves*2-1:0] wbs_bte_o,
|
||||
input wire [num_slaves*dw-1:0] wbs_dat_i,
|
||||
input wire [num_slaves-1:0] wbs_ack_i,
|
||||
input wire [num_slaves-1:0] wbs_err_i,
|
||||
input wire [num_slaves-1:0] wbs_rty_i);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Master/slave connection
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//Use parameter instead of localparam to work around a bug in Xilinx ISE
|
||||
parameter slave_sel_bits = num_slaves > 1 ? `CLOG2(num_slaves) : 1;
|
||||
|
||||
reg wbm_err;
|
||||
wire [slave_sel_bits-1:0] slave_sel;
|
||||
wire [num_slaves-1:0] match;
|
||||
|
||||
genvar idx;
|
||||
|
||||
generate
|
||||
for(idx=0; idx<num_slaves ; idx=idx+1) begin : addr_match
|
||||
assign match[idx] = (wbm_adr_i & MATCH_MASK[idx*aw+:aw]) == MATCH_ADDR[idx*aw+:aw];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
//
|
||||
// Find First 1 - Start from MSB and count downwards, returns 0 when no bit set
|
||||
//
|
||||
function [slave_sel_bits-1:0] ff1;
|
||||
input [num_slaves-1:0] in;
|
||||
integer i;
|
||||
|
||||
begin
|
||||
ff1 = 0;
|
||||
for (i = num_slaves-1; i >= 0; i=i-1) begin
|
||||
if (in[i])
|
||||
/* verilator lint_off WIDTH */
|
||||
ff1 = i;
|
||||
/* verilator lint_on WIDTH */
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
assign slave_sel = ff1(match);
|
||||
|
||||
always @(posedge wb_clk_i)
|
||||
wbm_err <= wbm_cyc_i & !(|match);
|
||||
|
||||
assign wbs_adr_o = {num_slaves{wbm_adr_i}};
|
||||
assign wbs_dat_o = {num_slaves{wbm_dat_i}};
|
||||
assign wbs_sel_o = {num_slaves{wbm_sel_i}};
|
||||
assign wbs_we_o = {num_slaves{wbm_we_i}};
|
||||
/* verilator lint_off WIDTH */
|
||||
|
||||
// Expand master CYC to slave bus width before shifting to one-hot select.
|
||||
// Shifting a 1-bit signal would otherwise zero out all but slave 0.
|
||||
assign wbs_cyc_o = match & ({num_slaves{wbm_cyc_i}} << slave_sel);
|
||||
/* verilator lint_on WIDTH */
|
||||
assign wbs_stb_o = {num_slaves{wbm_stb_i}};
|
||||
|
||||
assign wbs_cti_o = {num_slaves{wbm_cti_i}};
|
||||
assign wbs_bte_o = {num_slaves{wbm_bte_i}};
|
||||
|
||||
assign wbm_dat_o = wbs_dat_i[slave_sel*dw+:dw];
|
||||
assign wbm_ack_o = wbs_ack_i[slave_sel];
|
||||
assign wbm_err_o = wbs_err_i[slave_sel] | wbm_err;
|
||||
assign wbm_rty_o = wbs_rty_i[slave_sel];
|
||||
|
||||
endmodule
|
||||
74
rtl/wb/wb_timer.v
Normal file
74
rtl/wb/wb_timer.v
Normal file
@@ -0,0 +1,74 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module wb_countdown_timer #(
|
||||
parameter WIDTH = 32, // counter width (<=32 makes bus mapping easy)
|
||||
parameter DIVIDER = 0 // optional prescaler: tick every 2^DIVIDER cycles
|
||||
)(
|
||||
input wire i_clk,
|
||||
input wire i_rst,
|
||||
output reg o_irq,
|
||||
|
||||
input wire [31:0] i_wb_dat,
|
||||
output reg [31:0] o_wb_dat,
|
||||
input wire i_wb_we,
|
||||
input wire i_wb_cyc,
|
||||
input wire i_wb_stb,
|
||||
output wire o_wb_ack
|
||||
);
|
||||
|
||||
// One-cycle acknowledge on any valid WB access
|
||||
// (classic, zero-wait-state peripheral)
|
||||
assign o_wb_ack = i_wb_cyc & i_wb_stb;
|
||||
|
||||
// Internal countdown and prescaler
|
||||
reg [WIDTH-1:0] counter;
|
||||
reg [DIVIDER:0] presc; // enough bits to count up to 2^DIVIDER-1
|
||||
wire tick = (DIVIDER == 0) ? 1'b1 : (presc[DIVIDER] == 1'b1);
|
||||
|
||||
// Readback: expose the current counter value
|
||||
always @(*) begin
|
||||
o_wb_dat = 32'd0;
|
||||
o_wb_dat[WIDTH-1:0] = counter;
|
||||
end
|
||||
|
||||
// Main logic
|
||||
always @(posedge i_clk) begin
|
||||
if (i_rst) begin
|
||||
counter <= {WIDTH{1'b0}};
|
||||
presc <= { (DIVIDER+1){1'b0} };
|
||||
o_irq <= 1'b0;
|
||||
end else begin
|
||||
// Default prescaler behavior
|
||||
if (DIVIDER != 0) begin
|
||||
if (counter != 0 && !o_irq)
|
||||
presc <= presc + 1'b1;
|
||||
else
|
||||
presc <= { (DIVIDER+1){1'b0} };
|
||||
end
|
||||
|
||||
// Wishbone write: load counter and clear IRQ
|
||||
if (o_wb_ack && i_wb_we) begin
|
||||
counter <= i_wb_dat[WIDTH-1:0];
|
||||
o_irq <= 1'b0;
|
||||
|
||||
// reset prescaler on (re)start or stop
|
||||
presc <= { (DIVIDER+1){1'b0} };
|
||||
|
||||
end else begin
|
||||
// Countdown when running (counter>0), not already IRQ'd
|
||||
if (!o_irq && counter != 0) begin
|
||||
if (tick) begin
|
||||
if (counter == 1) begin
|
||||
counter <= {WIDTH{1'b0}};
|
||||
o_irq <= 1'b1; // sticky until next write
|
||||
presc <= { (DIVIDER+1){1'b0} };
|
||||
end else begin
|
||||
counter <= counter - 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
45
sim/tb/tb_top_generic.v
Normal file
45
sim/tb/tb_top_generic.v
Normal file
@@ -0,0 +1,45 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_top_generic();
|
||||
reg aclk;
|
||||
reg aresetn;
|
||||
|
||||
wire led_green;
|
||||
wire led_red;
|
||||
wire [5:0] r2r;
|
||||
wire [7:0] LED;
|
||||
|
||||
// 100 MHz board input clock
|
||||
initial aclk = 1'b0;
|
||||
always #5 aclk = ~aclk;
|
||||
|
||||
// Hold reset low, then release
|
||||
initial begin
|
||||
aresetn = 1'b0;
|
||||
#200;
|
||||
aresetn = 1'b1;
|
||||
end
|
||||
|
||||
top_generic #(
|
||||
.sim(1)
|
||||
) dut (
|
||||
.aclk(aclk),
|
||||
.aresetn(aresetn),
|
||||
.led_green(led_green),
|
||||
.led_red(led_red),
|
||||
.r2r(r2r),
|
||||
.LED(LED)
|
||||
);
|
||||
|
||||
// Ensure firmware path resolves from repository root when simulating.
|
||||
defparam dut.mcu.memfile = "sw/sweep/sweep.hex";
|
||||
|
||||
initial begin
|
||||
$dumpfile("out.vcd");
|
||||
$dumpvars(0, tb_top_generic);
|
||||
|
||||
// Let firmware run for a while.
|
||||
#5_000_000;
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
157
sim/tb/tb_wb_timer.v
Normal file
157
sim/tb/tb_wb_timer.v
Normal file
@@ -0,0 +1,157 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_wb_timer();
|
||||
localparam integer CLK_HALF_NS = 5;
|
||||
|
||||
reg clk;
|
||||
reg rst;
|
||||
|
||||
reg [31:0] i_wb_dat;
|
||||
wire [31:0] o_wb_dat;
|
||||
reg i_wb_we;
|
||||
reg i_wb_cyc;
|
||||
reg i_wb_stb;
|
||||
wire o_wb_ack;
|
||||
wire o_irq;
|
||||
|
||||
integer errors;
|
||||
|
||||
wb_countdown_timer #(
|
||||
.WIDTH(8),
|
||||
.DIVIDER(0)
|
||||
) dut (
|
||||
.i_clk (clk),
|
||||
.i_rst (rst),
|
||||
.o_irq (o_irq),
|
||||
.i_wb_dat(i_wb_dat),
|
||||
.o_wb_dat(o_wb_dat),
|
||||
.i_wb_we (i_wb_we),
|
||||
.i_wb_cyc(i_wb_cyc),
|
||||
.i_wb_stb(i_wb_stb),
|
||||
.o_wb_ack(o_wb_ack)
|
||||
);
|
||||
|
||||
always #CLK_HALF_NS clk = ~clk;
|
||||
|
||||
task wb_write;
|
||||
input [31:0] data;
|
||||
begin
|
||||
@(negedge clk);
|
||||
i_wb_dat = data;
|
||||
i_wb_we = 1'b1;
|
||||
i_wb_cyc = 1'b1;
|
||||
i_wb_stb = 1'b1;
|
||||
|
||||
#1;
|
||||
if (!o_wb_ack) begin
|
||||
$display("FAIL: WB write ACK not asserted when cyc/stb high");
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
@(negedge clk);
|
||||
i_wb_we = 1'b0;
|
||||
i_wb_cyc = 1'b0;
|
||||
i_wb_stb = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task wb_read_check;
|
||||
input [31:0] expected;
|
||||
begin
|
||||
@(negedge clk);
|
||||
i_wb_we = 1'b0;
|
||||
i_wb_cyc = 1'b1;
|
||||
i_wb_stb = 1'b1;
|
||||
|
||||
#1;
|
||||
if (!o_wb_ack) begin
|
||||
$display("FAIL: WB read ACK not asserted when cyc/stb high");
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
if (o_wb_dat !== expected) begin
|
||||
$display("FAIL: WB read mismatch. got=0x%08h expected=0x%08h", o_wb_dat, expected);
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
@(negedge clk);
|
||||
i_wb_cyc = 1'b0;
|
||||
i_wb_stb = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task wait_irq_with_timeout;
|
||||
input integer max_cycles;
|
||||
integer k;
|
||||
begin
|
||||
k = 0;
|
||||
while ((o_irq !== 1'b1) && (k < max_cycles)) begin
|
||||
@(posedge clk);
|
||||
k = k + 1;
|
||||
end
|
||||
|
||||
if (o_irq !== 1'b1) begin
|
||||
$display("FAIL: IRQ did not assert within %0d cycles", max_cycles);
|
||||
errors = errors + 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
$dumpfile("out.vcd");
|
||||
$dumpvars;
|
||||
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
i_wb_dat = 32'd0;
|
||||
i_wb_we = 1'b0;
|
||||
i_wb_cyc = 1'b0;
|
||||
i_wb_stb = 1'b0;
|
||||
errors = 0;
|
||||
|
||||
$display("---------------------------------------");
|
||||
$display("Testbench: wb_countdown_timer");
|
||||
$display("---------------------------------------");
|
||||
|
||||
repeat (3) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
repeat (1) @(posedge clk);
|
||||
|
||||
if (o_irq !== 1'b0) begin
|
||||
$display("FAIL: IRQ should be low after reset");
|
||||
errors = errors + 1;
|
||||
end
|
||||
wb_read_check(32'd0);
|
||||
|
||||
// Load timer and check readback at a deterministic point
|
||||
wb_write(32'd5);
|
||||
@(posedge clk);
|
||||
wb_read_check(32'd4);
|
||||
|
||||
// Wait until expiration, then verify sticky IRQ behavior
|
||||
wait_irq_with_timeout(6);
|
||||
wb_read_check(32'd0);
|
||||
|
||||
repeat (2) @(posedge clk);
|
||||
if (o_irq !== 1'b1) begin
|
||||
$display("FAIL: IRQ should remain sticky until next write");
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
// A new write must clear IRQ and restart the counter
|
||||
wb_write(32'd2);
|
||||
if (o_irq !== 1'b0) begin
|
||||
$display("FAIL: IRQ should clear on write");
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
wait_irq_with_timeout(3);
|
||||
|
||||
if (errors == 0)
|
||||
$display("PASS: wb_timer basic functionality verified");
|
||||
else
|
||||
$display("FAIL: wb_timer testbench found %0d error(s)", errors);
|
||||
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -3,7 +3,7 @@ ENTRY(_start)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8064
|
||||
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8192
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -29,5 +29,5 @@ SECTIONS
|
||||
} > RAM
|
||||
|
||||
. = ALIGN(4);
|
||||
__stack_top = ORIGIN(RAM) + LENGTH(RAM);
|
||||
__stack_top = ORIGIN(RAM) + LENGTH(RAM) - 256;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ void main(){
|
||||
for(;;){
|
||||
for(int i=1000; i<10000; i++){
|
||||
*R_FREQ = i;
|
||||
for(int j=0; j<100; j++) asm volatile("nop");
|
||||
// for(int j=0; j<100; j++) asm volatile("nop");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user