Skip to content

Commit

Permalink
PG: initial version copied from Ale
Browse files Browse the repository at this point in the history
  • Loading branch information
thommythomaso committed Sep 15, 2023
1 parent e80b8c8 commit 472b247
Show file tree
Hide file tree
Showing 7 changed files with 442 additions and 9 deletions.
2 changes: 2 additions & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ sources:
- hw/bootrom/cheshire_bootrom.sv
- hw/regs/cheshire_reg_pkg.sv
- hw/regs/cheshire_reg_top.sv
- hw/cheshire_ext_playground_pkg.sv
- hw/cheshire_ext_playground.sv
- hw/cheshire_pkg.sv
- hw/cheshire_soc.sv

Expand Down
277 changes: 277 additions & 0 deletions hw/cheshire_ext_playground.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
//
// Alessandro Ottaviano <[email protected]>

`include "cheshire/typedef.svh"
`include "axi/typedef.svh"

module cheshire_ext_playground
import cheshire_pkg::*;
import cheshire_ext_playground_pkg::*;
#(
// Cheshire config
parameter cheshire_cfg_t Cfg = '0,
// Interconnect types (must agree with Cheshire config)
parameter type axi_ext_mst_req_t = logic,
parameter type axi_ext_mst_rsp_t = logic,
parameter type axi_ext_slv_req_t = logic,
parameter type axi_ext_slv_rsp_t = logic,
parameter type reg_ext_req_t = logic,
parameter type reg_ext_rsp_t = logic
) (
input logic clk_i,
input logic rst_ni,
input logic test_mode_i,
input logic rtc_i,
// External AXI crossbar ports
output axi_ext_mst_req_t [iomsb(Cfg.AxiExtNumMst):0] axi_ext_mst_req_o,
input axi_ext_mst_rsp_t [iomsb(Cfg.AxiExtNumMst):0] axi_ext_mst_rsp_i,
input axi_ext_slv_req_t [iomsb(Cfg.AxiExtNumSlv):0] axi_ext_slv_req_i,
output axi_ext_slv_rsp_t [iomsb(Cfg.AxiExtNumSlv):0] axi_ext_slv_rsp_o,
// External reg demux slaves
input reg_ext_req_t [iomsb(Cfg.RegExtNumSlv):0] reg_ext_slv_req_i,
output reg_ext_rsp_t [iomsb(Cfg.RegExtNumSlv):0] reg_ext_slv_rsp_o
);

// General parameters and defines
`CHESHIRE_TYPEDEF_ALL(chs_playgnd_, Cfg)

// Generate indices and get maps for all ports
localparam axi_in_t AxiIn = gen_axi_in(Cfg);
localparam axi_out_t AxiOut = gen_axi_out(Cfg);

localparam int unsigned ChsPlaygndAxiSlvIdWidth = Cfg.AxiMstIdWidth + $clog2(AxiIn.num_in);

// tied-off
assign axi_ext_slv_rsp_o[PeriphsSlvIdx] = '0;


///////////////////////////////////////
// DSAs' traffic generators (dma(s)) //
///////////////////////////////////////

chs_playgnd_axi_slv_req_t [ChsPlaygndNumDsaDma-1:0] dsa_dma_amo_req, dsa_dma_cut_req;
chs_playgnd_axi_slv_rsp_t [ChsPlaygndNumDsaDma-1:0] dsa_dma_amo_rsp, dsa_dma_cut_rsp;

if (ChsPlaygndNumSlvDevices != Cfg.AxiExtNumSlv)
$fatal(1, "the number of slave devices must be equal to the number of slave AXI ports");

if (ChsPlaygndNumMstDevices != Cfg.AxiExtNumMst)
$fatal(1, "the number of master devices must be equal to the number of master AXI ports");

for (genvar i=0; i<ChsPlaygndNumDsaDma; i++) begin : gen_dsa_dmas
axi_riscv_atomics_structs #(
.AxiAddrWidth ( Cfg.AddrWidth ),
.AxiDataWidth ( Cfg.AxiDataWidth ),
.AxiIdWidth ( ChsPlaygndAxiSlvIdWidth ),
.AxiUserWidth ( Cfg.AxiUserWidth ),
.AxiMaxReadTxns ( Cfg.DmaConfMaxReadTxns ),
.AxiMaxWriteTxns ( Cfg.DmaConfMaxWriteTxns ),
.AxiUserAsId ( 1 ),
.AxiUserIdMsb ( Cfg.AxiUserAmoMsb ),
.AxiUserIdLsb ( Cfg.AxiUserAmoLsb ),
.RiscvWordWidth ( 64 ),
.NAxiCuts ( Cfg.DmaConfAmoNumCuts ),
.axi_req_t ( chs_playgnd_axi_slv_req_t ),
.axi_rsp_t ( chs_playgnd_axi_slv_rsp_t )
) i_dsa_dma_conf_atomics (
.clk_i,
.rst_ni,
.axi_slv_req_i ( axi_ext_slv_req_i[Dsa0SlvIdx + i] ),
.axi_slv_rsp_o ( axi_ext_slv_rsp_o[Dsa0SlvIdx + i] ),
.axi_mst_req_o ( dsa_dma_amo_req[i] ),
.axi_mst_rsp_i ( dsa_dma_amo_rsp[i] )
);

axi_cut #(
.Bypass ( ~Cfg.DmaConfAmoPostCut ),
.aw_chan_t ( chs_playgnd_axi_slv_aw_chan_t ),
.w_chan_t ( chs_playgnd_axi_slv_w_chan_t ),
.b_chan_t ( chs_playgnd_axi_slv_b_chan_t ),
.ar_chan_t ( chs_playgnd_axi_slv_ar_chan_t ),
.r_chan_t ( chs_playgnd_axi_slv_r_chan_t ),
.axi_req_t ( chs_playgnd_axi_slv_req_t ),
.axi_resp_t ( chs_playgnd_axi_slv_rsp_t )
) i_dsa_dma_conf_atomics_cut (
.clk_i,
.rst_ni,
.slv_req_i ( dsa_dma_amo_req[i] ),
.slv_resp_o ( dsa_dma_amo_rsp[i] ),
.mst_req_o ( dsa_dma_cut_req[i] ),
.mst_resp_i ( dsa_dma_cut_rsp[i] )
);

chs_playgnd_axi_mst_req_t [ChsPlaygndNumDsaDma-1:0] axi_dsa_dma_req;

always_comb begin
axi_ext_mst_req_o[Dsa0MstIdx + i] = axi_dsa_dma_req[i];
axi_ext_mst_req_o[Dsa0MstIdx + i].aw.user = Cfg.AxiUserDefault;
axi_ext_mst_req_o[Dsa0MstIdx + i].w.user = Cfg.AxiUserDefault;
axi_ext_mst_req_o[Dsa0MstIdx + i].ar.user = Cfg.AxiUserDefault;
end

dma_core_wrap #(
.AxiAddrWidth ( Cfg.AddrWidth ),
.AxiDataWidth ( Cfg.AxiDataWidth ),
.AxiIdWidth ( Cfg.AxiMstIdWidth ),
.AxiUserWidth ( Cfg.AxiUserWidth ),
.AxiSlvIdWidth ( ChsPlaygndAxiSlvIdWidth ),
.NumAxInFlight ( Cfg.DmaNumAxInFlight ),
.MemSysDepth ( Cfg.DmaMemSysDepth ),
.JobFifoDepth ( Cfg.DmaJobFifoDepth ),
.RAWCouplingAvail ( Cfg.DmaRAWCouplingAvail ),
.IsTwoD ( Cfg.DmaConfEnableTwoD ),
.axi_mst_req_t ( chs_playgnd_axi_mst_req_t ),
.axi_mst_rsp_t ( chs_playgnd_axi_mst_rsp_t ),
.axi_slv_req_t ( chs_playgnd_axi_slv_req_t ),
.axi_slv_rsp_t ( chs_playgnd_axi_slv_rsp_t )
) i_dsa_dma (
.clk_i,
.rst_ni,
.testmode_i ( test_mode_i ),
.axi_mst_req_o ( axi_dsa_dma_req[i] ),
.axi_mst_rsp_i ( axi_ext_mst_rsp_i[Dsa0MstIdx + i] ),
.axi_slv_req_i ( dsa_dma_cut_req[i] ),
.axi_slv_rsp_o ( dsa_dma_cut_rsp[i] )
);
end

// Memories
localparam int unsigned ChsPlaygndNumAxiMems = 3;

// axi interface
chs_playgnd_axi_slv_req_t [ChsPlaygndNumAxiMems-1:0] chs_playgnd_axi_mem_req;
chs_playgnd_axi_slv_rsp_t [ChsPlaygndNumAxiMems-1:0] chs_playgnd_axi_mem_rsp;

// mem interface

localparam int unsigned NumWords = 128*1024;
localparam int unsigned MemAddrWidth = $clog2(NumWords);
logic [ChsPlaygndNumAxiMems-1:0] mem_req, mem_gnt, mem_we, mem_rvalid, mem_rvalid_q;
logic [ChsPlaygndNumAxiMems-1:0] [Cfg.AddrWidth-1:0] mem_addr;
logic [ChsPlaygndNumAxiMems-1:0] [Cfg.AxiDataWidth-1:0] mem_wdata, mem_rdata;
logic [ChsPlaygndNumAxiMems-1:0] [Cfg.AxiDataWidth/8-1:0] mem_strb;

always_ff @(posedge clk_i or negedge rst_ni) begin : proc_delay
if(~rst_ni) begin
mem_rvalid_q <= 0;
end else begin
mem_rvalid_q <= mem_rvalid;
end
end

assign mem_gnt = '1; //mem_req;
assign mem_rvalid = mem_req;

for (genvar i=0; i<ChsPlaygndNumAxiMems; i++ ) begin : gen_axi_mems

chs_playgnd_axi_slv_req_t axi_mem_req;
chs_playgnd_axi_slv_rsp_t axi_mem_rsp;

axi_fifo #(
.Depth(3),
.FallThrough (1'b0),
.aw_chan_t (chs_playgnd_axi_slv_aw_chan_t),
.w_chan_t (chs_playgnd_axi_slv_w_chan_t),
.b_chan_t (chs_playgnd_axi_slv_b_chan_t),
.ar_chan_t (chs_playgnd_axi_slv_ar_chan_t),
.r_chan_t (chs_playgnd_axi_slv_r_chan_t),
.axi_req_t (chs_playgnd_axi_slv_req_t),
.axi_resp_t(chs_playgnd_axi_slv_rsp_t)
) i_axi_fifo (
.clk_i,
.rst_ni,
.test_i ( 1'b0 ),
.slv_req_i ( axi_ext_slv_req_i[i+MemWriteSlvIdx] ),
.slv_resp_o ( axi_ext_slv_rsp_o[i+MemWriteSlvIdx] ),
.mst_req_o ( axi_mem_req ),
.mst_resp_i ( axi_mem_rsp )
);

axi_to_mem_interleaved #(
.axi_req_t (chs_playgnd_axi_slv_req_t),
.axi_resp_t (chs_playgnd_axi_slv_rsp_t),
.AddrWidth (Cfg.AddrWidth),
.DataWidth (Cfg.AxiDataWidth),
.IdWidth (ChsPlaygndAxiSlvIdWidth),
.NumBanks (1),
.BufDepth (128),
.HideStrb (0),
.OutFifoDepth (128)
) chs_playgnd_i_axi_to_mem (
.clk_i,
.rst_ni,
.busy_o ( ),
.axi_req_i ( axi_mem_req),
.axi_resp_o ( axi_mem_rsp),
.mem_req_o ( mem_req[i]),
.mem_gnt_i ( mem_gnt[i]),
.mem_addr_o ( mem_addr[i]),
.mem_wdata_o ( mem_wdata[i]),
.mem_strb_o ( mem_strb[i]),
.mem_atop_o ( ),
.mem_we_o ( mem_we[i]),
.mem_rvalid_i ( mem_rvalid_q[i]),
.mem_rdata_i ( mem_rdata[i])
);

tc_sram #(
.NumWords ( NumWords ), // Number of Words in data array
.DataWidth ( Cfg.AxiDataWidth ), // Data signal width
.ByteWidth ( 8 ), // Width of a data byte
.NumPorts ( 1 ), // Number of read and write ports
.SimInit ( "random" ),
.Latency ( 1 ) // Latency when the read data is available
) chs_playgnd_i_tc_sram (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
.req_i ( mem_req[i] ), // request
.we_i ( mem_we[i] ), // write enable
.addr_i ( mem_addr[i][MemAddrWidth-1:0] ), // request address
.wdata_i ( mem_wdata[i] ), // write data
.be_i ( mem_strb[i] ), // write byte enable

.rdata_o ( mem_rdata[i] ) // read data
);
// axi_sim_mem #(
// .AddrWidth ( Cfg.AddrWidth ),
// .DataWidth ( Cfg.AxiDataWidth ),
// .IdWidth ( ChsPlaygndAxiSlvIdWidth ),
// .UserWidth ( Cfg.AxiUserWidth ),
// .axi_req_t ( chs_playgnd_axi_slv_req_t ),
// .axi_rsp_t ( chs_playgnd_axi_slv_rsp_t ),
// .WarnUninitialized ( 0 ),
// .ClearErrOnAccess ( 1 ),
// .ApplDelay ( 5ns * 0.1 ),
// .AcqDelay ( 5ns * 0.9 )
// ) chs_playgnd_i_axi_sim_mem (
// .clk_i,
// .rst_ni,
// .axi_req_i ( axi_ext_slv_req_i[i+MemWriteSlvIdx] ),
// .axi_rsp_o ( axi_ext_slv_rsp_o[i+MemWriteSlvIdx] ),
// .mon_w_valid_o ( ),
// .mon_w_addr_o ( ),
// .mon_w_data_o ( ),
// .mon_w_id_o ( ),
// .mon_w_user_o ( ),
// .mon_w_beat_count_o ( ),
// .mon_w_last_o ( ),
// .mon_r_valid_o ( ),
// .mon_r_addr_o ( ),
// .mon_r_data_o ( ),
// .mon_r_id_o ( ),
// .mon_r_user_o ( ),
// .mon_r_beat_count_o ( ),
// .mon_r_last_o ( )
// );
//
// initial begin
// for (int j=0; j<128*1024; j++) begin
// chs_playgnd_i_axi_sim_mem.mem[MemWriteBase + 'h10000000*i + j] = {'d8{$urandom()}};
// end
// end
end

Check warning on line 276 in hw/cheshire_ext_playground.sv

View workflow job for this annotation

GitHub Actions / verible-verilog-lint

[verible-verilog-lint] hw/cheshire_ext_playground.sv#L276

Remove trailing spaces. [Style: trailing-spaces] [no-trailing-spaces]
Raw output
message:"Remove trailing spaces. [Style: trailing-spaces] [no-trailing-spaces]"  location:{path:"hw/cheshire_ext_playground.sv"  range:{start:{line:276  column:1}}}  severity:WARNING  source:{name:"verible-verilog-lint"  url:"https://github.com/chipsalliance/verible"}  suggestions:{range:{start:{line:276  column:1}  end:{line:277}}  text:"\n"}
endmodule
91 changes: 91 additions & 0 deletions hw/cheshire_ext_playground_pkg.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
//
// Alessandro Ottaviano <[email protected]>

`include "cheshire/typedef.svh"

package cheshire_ext_playground_pkg;

import cheshire_pkg::*;

// Number of DSA DMAs
localparam int unsigned ChsPlaygndNumDsaDma = 1;

// Number of slave peripherals
localparam int unsigned ChsPlaygndNumPeriphs = 1;

// Number of slave mems

Check warning on line 19 in hw/cheshire_ext_playground_pkg.sv

View workflow job for this annotation

GitHub Actions / verible-verilog-lint

[verible-verilog-lint] hw/cheshire_ext_playground_pkg.sv#L19

Remove trailing spaces. [Style: trailing-spaces] [no-trailing-spaces]
Raw output
message:"Remove trailing spaces. [Style: trailing-spaces] [no-trailing-spaces]"  location:{path:"hw/cheshire_ext_playground_pkg.sv"  range:{start:{line:19  column:26}}}  severity:WARNING  source:{name:"verible-verilog-lint"  url:"https://github.com/chipsalliance/verible"}  suggestions:{range:{start:{line:19  column:26}  end:{line:21}}  text:"  // Number of slave mems\n\n"}
localparam int unsigned ChsPlaygndNumAxiMems = 3;

Check warning on line 21 in hw/cheshire_ext_playground_pkg.sv

View workflow job for this annotation

GitHub Actions / verible-verilog-lint

[verible-verilog-lint] hw/cheshire_ext_playground_pkg.sv#L21

Remove trailing spaces. [Style: trailing-spaces] [no-trailing-spaces]
Raw output
message:"Remove trailing spaces. [Style: trailing-spaces] [no-trailing-spaces]"  location:{path:"hw/cheshire_ext_playground_pkg.sv"  range:{start:{line:21  column:1}}}  severity:WARNING  source:{name:"verible-verilog-lint"  url:"https://github.com/chipsalliance/verible"}
// Total number of master and slaves
localparam int unsigned ChsPlaygndNumSlvDevices = ChsPlaygndNumDsaDma + ChsPlaygndNumPeriphs + ChsPlaygndNumAxiMems;

Check warning on line 23 in hw/cheshire_ext_playground_pkg.sv

View workflow job for this annotation

GitHub Actions / verible-verilog-lint

[verible-verilog-lint] hw/cheshire_ext_playground_pkg.sv#L23

Line length exceeds max: 100; is: 118 [Style: line-length] [line-length]
Raw output
message:"Line length exceeds max: 100; is: 118 [Style: line-length] [line-length]"  location:{path:"hw/cheshire_ext_playground_pkg.sv"  range:{start:{line:23  column:101}}}  severity:WARNING  source:{name:"verible-verilog-lint"  url:"https://github.com/chipsalliance/verible"}
localparam int unsigned ChsPlaygndNumMstDevices = ChsPlaygndNumDsaDma;

// Narrow AXI widths
localparam int unsigned ChsPlaygndAxiNarrowAddrWidth = 32;
localparam int unsigned ChsPlaygndAxiNarrowDataWidth = 32;
localparam int unsigned ChsPlaygndAxiNarrowStrobe = ChsPlaygndAxiNarrowDataWidth/8;

// Narrow AXI types
typedef logic [ ChsPlaygndAxiNarrowAddrWidth-1:0] chs_playgnd_nar_addrw_t;
typedef logic [ ChsPlaygndAxiNarrowDataWidth-1:0] chs_playgnd_nar_dataw_t;
typedef logic [ ChsPlaygndAxiNarrowStrobe-1:0] chs_playgnd_nar_strb_t;

// External AXI slaves indexes
typedef enum byte_bt {
PeriphsSlvIdx = 'd0,
Dsa0SlvIdx = 'd1,
MemWriteSlvIdx = 'd2,
MemDmaReadSlvIdx = 'd3,
MemCoreReadSlvIdx = 'd4
} axi_slv_idx_t;

// External AXI masters indexes
typedef enum byte_bt {
Dsa0MstIdx = 'd0
} axi_mst_idx_t;

typedef enum doub_bt {
PeriphsBase = 'h0000_0000_2000_1000,
Dsa0Base = 'h0000_0000_3000_1000,
MemWriteBase = 'h0000_0000_4000_0000,
MemDmaReadBase = 'h0000_0000_5000_0000,
MemCoreReadBase = 'h0000_0000_6000_0000
} axi_start_t;

// AXI Slave Sizes
localparam doub_bt PeriphsSize = 'h0000_0000_0000_9000;
localparam doub_bt Dsa0Size = 'h0000_0000_0000_9000;
localparam doub_bt MemWriteSize = 'h0000_0000_1000_0000;
localparam doub_bt MemDmaReadSize = 'h0000_0000_1000_0000;
localparam doub_bt MemCoreReadSize = 'h0000_0000_1000_0000;

typedef enum doub_bt {
PeriphsEnd = PeriphsBase + PeriphsSize,
Dsa0End = Dsa0Base + Dsa0Size,
MemWriteEnd = MemWriteBase + MemWriteSize,
MemDmaReadEnd = MemDmaReadBase + MemDmaReadSize,
MemCoreReadEnd = MemCoreReadBase + MemCoreReadSize
} axi_end_t;

function automatic cheshire_cfg_t gen_playground_cfg();
cheshire_cfg_t ret = DefaultCfg;
ret.Cva6ExtCieLength = 'h6000_0000; // all above 0x3000_0000 cached
ret.Cva6ExtCieOnTop = 1;
// External AXI ports (at most 8 ports and rules)
ret.AxiExtNumMst = 1; // For the playground, traffic DMA(s)
ret.AxiExtNumSlv = 5; // For the playground, traffic DMA(s) (config port), system timer
ret.AxiExtNumRules = 5;
// External AXI region map
ret.AxiExtRegionIdx = '{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MemCoreReadSlvIdx, MemDmaReadSlvIdx, MemWriteSlvIdx, Dsa0SlvIdx, PeriphsSlvIdx };

Check warning on line 82 in hw/cheshire_ext_playground_pkg.sv

View workflow job for this annotation

GitHub Actions / verible-verilog-lint

[verible-verilog-lint] hw/cheshire_ext_playground_pkg.sv#L82

Line length exceeds max: 100; is: 145 [Style: line-length] [line-length]
Raw output
message:"Line length exceeds max: 100; is: 145 [Style: line-length] [line-length]"  location:{path:"hw/cheshire_ext_playground_pkg.sv"  range:{start:{line:82  column:101}}}  severity:WARNING  source:{name:"verible-verilog-lint"  url:"https://github.com/chipsalliance/verible"}
ret.AxiExtRegionStart = '{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MemCoreReadBase, MemDmaReadBase, MemWriteBase, Dsa0Base, PeriphsBase };

Check warning on line 83 in hw/cheshire_ext_playground_pkg.sv

View workflow job for this annotation

GitHub Actions / verible-verilog-lint

[verible-verilog-lint] hw/cheshire_ext_playground_pkg.sv#L83

Line length exceeds max: 100; is: 145 [Style: line-length] [line-length]
Raw output
message:"Line length exceeds max: 100; is: 145 [Style: line-length] [line-length]"  location:{path:"hw/cheshire_ext_playground_pkg.sv"  range:{start:{line:83  column:101}}}  severity:WARNING  source:{name:"verible-verilog-lint"  url:"https://github.com/chipsalliance/verible"}
ret.AxiExtRegionEnd = '{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MemCoreReadEnd, MemDmaReadEnd, MemWriteEnd, Dsa0End, PeriphsEnd };

Check warning on line 84 in hw/cheshire_ext_playground_pkg.sv

View workflow job for this annotation

GitHub Actions / verible-verilog-lint

[verible-verilog-lint] hw/cheshire_ext_playground_pkg.sv#L84

Line length exceeds max: 100; is: 145 [Style: line-length] [line-length]
Raw output
message:"Line length exceeds max: 100; is: 145 [Style: line-length] [line-length]"  location:{path:"hw/cheshire_ext_playground_pkg.sv"  range:{start:{line:84  column:101}}}  severity:WARNING  source:{name:"verible-verilog-lint"  url:"https://github.com/chipsalliance/verible"}
ret.BusErr = 0;
return ret;
endfunction

localparam cheshire_cfg_t ChsPlaygndCfg = gen_playground_cfg();

endpackage
6 changes: 3 additions & 3 deletions hw/cheshire_soc.sv
Original file line number Diff line number Diff line change
Expand Up @@ -1470,9 +1470,9 @@ module cheshire_soc import cheshire_pkg::*; #(

end

if (!(Cfg.Dma && Cfg.BusErr)) begin : gen_dma_bus_err_tie
assign intr.intn.bus_err.dma = '0;
end
//if (!(Cfg.Dma && Cfg.BusErr)) begin : gen_dma_bus_err_tie
// assign intr.intn.bus_err.dma = '0;
//end

///////////////////
// Serial Link //
Expand Down
Loading

0 comments on commit 472b247

Please sign in to comment.