Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenXiangShan
XiangShan
提交
4fcc0784
X
XiangShan
项目概览
OpenXiangShan
/
XiangShan
12 个月 前同步成功
通知
1183
Star
3914
Fork
526
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
X
XiangShan
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
4fcc0784
编写于
12月 29, 2020
作者:
Y
Yinan Xu
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'origin/master' into opt-roq
上级
f28a4d4a
47f2011c
变更
19
隐藏空白更改
内联
并排
Showing
19 changed file
with
243 addition
and
227 deletion
+243
-227
rocket-chip
rocket-chip
+1
-1
src/main/scala/utils/Pipeline.scala
src/main/scala/utils/Pipeline.scala
+1
-0
src/main/scala/xiangshan/backend/dispatch/DispatchQueue.scala
...main/scala/xiangshan/backend/dispatch/DispatchQueue.scala
+1
-1
src/main/scala/xiangshan/backend/fu/fpu/divsqrt/DivSqrt.scala
...main/scala/xiangshan/backend/fu/fpu/divsqrt/DivSqrt.scala
+1
-0
src/main/scala/xiangshan/backend/fu/fpu/divsqrt/MantDivSqrt.scala
.../scala/xiangshan/backend/fu/fpu/divsqrt/MantDivSqrt.scala
+2
-0
src/main/scala/xiangshan/backend/issue/ReservationStationNew.scala
...scala/xiangshan/backend/issue/ReservationStationNew.scala
+25
-12
src/main/scala/xiangshan/backend/roq/Roq.scala
src/main/scala/xiangshan/backend/roq/Roq.scala
+9
-9
src/main/scala/xiangshan/cache/icache.scala
src/main/scala/xiangshan/cache/icache.scala
+25
-22
src/main/scala/xiangshan/cache/ptw.scala
src/main/scala/xiangshan/cache/ptw.scala
+3
-3
src/main/scala/xiangshan/frontend/BPU.scala
src/main/scala/xiangshan/frontend/BPU.scala
+4
-2
src/main/scala/xiangshan/frontend/Bim.scala
src/main/scala/xiangshan/frontend/Bim.scala
+2
-2
src/main/scala/xiangshan/frontend/Frontend.scala
src/main/scala/xiangshan/frontend/Frontend.scala
+16
-22
src/main/scala/xiangshan/frontend/IFU.scala
src/main/scala/xiangshan/frontend/IFU.scala
+70
-81
src/main/scala/xiangshan/frontend/Ibuffer.scala
src/main/scala/xiangshan/frontend/Ibuffer.scala
+23
-13
src/main/scala/xiangshan/mem/lsqueue/LoadQueue.scala
src/main/scala/xiangshan/mem/lsqueue/LoadQueue.scala
+1
-1
src/main/scala/xiangshan/mem/lsqueue/StoreQueue.scala
src/main/scala/xiangshan/mem/lsqueue/StoreQueue.scala
+1
-1
src/test/csrc/emu.cpp
src/test/csrc/emu.cpp
+3
-2
src/test/csrc/emu.h
src/test/csrc/emu.h
+1
-1
src/test/scala/xiangshan/frontend/IFUTest.scala
src/test/scala/xiangshan/frontend/IFUTest.scala
+54
-54
未找到文件。
rocket-chip
@
9cf18f11
比较
147bdcc4
...
9cf18f11
Subproject commit
147bdcc4a26c74e5d7a47e3d667d456699d6d11f
Subproject commit
9cf18f1173443e2542990c090a69bdd62d26228d
src/main/scala/utils/Pipeline.scala
浏览文件 @
4fcc0784
...
...
@@ -6,6 +6,7 @@ import chisel3.util._
object
PipelineConnect
{
def
apply
[
T
<:
Data
](
left
:
DecoupledIO
[
T
],
right
:
DecoupledIO
[
T
],
rightOutFire
:
Bool
,
isFlush
:
Bool
)
=
{
val
valid
=
RegInit
(
false
.
B
)
valid
.
suggestName
(
"pipeline_valid"
)
when
(
rightOutFire
)
{
valid
:=
false
.
B
}
when
(
left
.
valid
&&
right
.
ready
)
{
valid
:=
true
.
B
}
when
(
isFlush
)
{
valid
:=
false
.
B
}
...
...
src/main/scala/xiangshan/backend/dispatch/DispatchQueue.scala
浏览文件 @
4fcc0784
...
...
@@ -34,7 +34,7 @@ class DispatchQueue(size: Int, enqnum: Int, deqnum: Int) extends XSModule with H
val
tailPtr
=
RegInit
(
VecInit
((
0
until
enqnum
).
map
(
_
.
U
.
asTypeOf
(
new
CircularQueuePtr
(
size
)))))
val
tailPtrMask
=
UIntToMask
(
tailPtr
(
0
).
value
,
size
)
// valid entries counter
val
validCounter
=
RegInit
(
0.
U
(
log2Ceil
(
size
).
W
))
val
validCounter
=
RegInit
(
0.
U
(
log2Ceil
(
size
+
1
).
W
))
val
allowEnqueue
=
RegInit
(
true
.
B
)
val
isTrueEmpty
=
~
Cat
((
0
until
size
).
map
(
i
=>
stateEntries
(
i
)
===
s_valid
)).
orR
...
...
src/main/scala/xiangshan/backend/fu/fpu/divsqrt/DivSqrt.scala
浏览文件 @
4fcc0784
...
...
@@ -140,6 +140,7 @@ class DivSqrt extends FPUSubModule {
// 53 + 2 + 2 = 57 bits are needed, but 57 % log2(4) != 0, use 58 bits instead
val
mantDivSqrt
=
Module
(
new
MantDivSqrt
(
D_MANT_WIDTH
+
2
+
2
+
1
))
mantDivSqrt
.
io
.
kill
:=
kill
mantDivSqrt
.
io
.
out
.
ready
:=
true
.
B
mantDivSqrt
.
io
.
in
.
valid
:=
state
===
s_start
mantDivSqrt
.
io
.
in
.
bits
.
a
:=
Mux
(
isDivReg
||
aIsOddExp
,
Cat
(
aMantReg
,
0.
U
(
5.
W
)),
Cat
(
0.
U
(
1.
W
),
aMantReg
,
0.
U
(
4.
W
)))
...
...
src/main/scala/xiangshan/backend/fu/fpu/divsqrt/MantDivSqrt.scala
浏览文件 @
4fcc0784
...
...
@@ -11,6 +11,7 @@ class MantDivSqrt(len: Int) extends Module{
val
a
,
b
=
UInt
(
len
.
W
)
val
isDiv
=
Bool
()
}))
val
kill
=
Input
(
Bool
())
val
out
=
DecoupledIO
(
new
Bundle
()
{
val
quotient
=
UInt
(
len
.
W
)
val
isZeroRem
=
Bool
()
...
...
@@ -45,6 +46,7 @@ class MantDivSqrt(len: Int) extends Module{
when
(
io
.
out
.
fire
()){
state
:=
s_idle
}
}
}
when
(
io
.
kill
){
state
:=
s_idle
}
val
ws
,
wc
=
Reg
(
UInt
((
len
+
4
).
W
))
...
...
src/main/scala/xiangshan/backend/issue/ReservationStationNew.scala
浏览文件 @
4fcc0784
...
...
@@ -167,32 +167,56 @@ class ReservationStationCtrl
}
// redirect and feedback
// redirect and feedback && wakeup
//
for
(
i
<-
0
until
iqSize
)
{
val
cnt
=
cntQueue
(
idxQueue
(
i
))
when
(!(
deqIdx
===
i
.
U
&&
deqValid
))
{
if
(
i
!=
0
)
{
// TODO: combine the two case
val
nextIdx
=
i
.
U
-
moveMask
(
i
-
1
)
// cnt
when
(
stateQueue
(
i
)===
s_replay
)
{
when
(
cnt
===
0.
U
)
{
stateQueue
(
nextIdx
)
:=
s_valid
}
.
otherwise
{
cnt
:=
cnt
-
1.
U
}
}
// feedback
when
(
fbMatchVec
(
i
))
{
stateQueue
(
nextIdx
)
:=
Mux
(
fbHit
,
s_idle
,
s_replay
)
cnt
:=
Mux
(
fbHit
,
cnt
,
(
replayDelay
-
1
).
U
)
}
// redirect
when
(
redHitVec
(
i
))
{
stateQueue
(
nextIdx
)
:=
s_idle
}
// wakeup
val
hitVec
=
io
.
data
.
srcUpdate
(
idxQueue
(
i
))
for
(
j
<-
0
until
srcNum
)
{
when
(
hitVec
(
j
)
&&
validQueue
(
i
))
{
srcQueue
(
nextIdx
)(
j
)
:=
true
.
B
XSDebug
(
p
"srcHit: i:${i.U} j:${j.U} moveMask(${i.U}):${moveMask(i)} nextIdx:${nextIdx}"
)
}
}
}
else
{
when
(!
moveMask
(
i
))
{
val
nextIdx
=
i
// cnt
when
(
stateQueue
(
i
)===
s_replay
)
{
when
(
cnt
===
0.
U
)
{
stateQueue
(
nextIdx
)
:=
s_valid
}
.
otherwise
{
cnt
:=
cnt
-
1.
U
}
}
// feedback
when
(
fbMatchVec
(
i
))
{
stateQueue
(
nextIdx
)
:=
Mux
(
fbHit
,
s_idle
,
s_replay
)
cnt
:=
Mux
(
fbHit
,
cnt
,
(
replayDelay
-
1
).
U
)
}
// redirect
when
(
redHitVec
(
i
))
{
stateQueue
(
nextIdx
)
:=
s_idle
}
// wakeup
val
hitVec
=
io
.
data
.
srcUpdate
(
idxQueue
(
i
))
for
(
j
<-
0
until
srcNum
)
{
when
(
hitVec
(
j
)
&&
validQueue
(
i
))
{
srcQueue
(
nextIdx
)(
j
)
:=
true
.
B
XSDebug
(
p
"srcHit: i:${i.U} j:${j.U} moveMask(${i.U}):${moveMask(i)} nextIdx:${nextIdx}"
)
}
}
}}
}
}
...
...
@@ -235,17 +259,6 @@ class ReservationStationCtrl
p
"type:${srcTypeSeq(2)}\n"
)
}
// wakeup
for
(
i
<-
0
until
IssQueSize
)
{
val
hitVec
=
io
.
data
.
srcUpdate
(
idxQueue
(
i
))
for
(
j
<-
0
until
srcNum
)
{
when
(
hitVec
(
j
)
&&
validQueue
(
i
))
{
srcQueue
(
i
.
U
-
moveMask
(
i
))(
j
)
:=
true
.
B
XSDebug
(
p
"srcHit: i:${i.U} j:${j.U}\n"
)
}
}
}
// other to Data
io
.
data
.
enqPtr
:=
idxQueue
(
Mux
(
tailPtr
.
flag
,
deqIdx
,
tailPtr
.
value
))
io
.
data
.
deqPtr
.
valid
:=
selValid
...
...
src/main/scala/xiangshan/backend/roq/Roq.scala
浏览文件 @
4fcc0784
...
...
@@ -237,7 +237,7 @@ class Roq(numWbPorts: Int) extends XSModule with HasCircularQueuePtrHelper {
val
deqPtrVec
=
Wire
(
Vec
(
CommitWidth
,
new
RoqPtr
))
val
walkPtrVec
=
Reg
(
Vec
(
CommitWidth
,
new
RoqPtr
))
val
validCounter
=
RegInit
(
0.
U
(
log2Ceil
(
RoqSize
).
W
))
val
validCounter
=
RegInit
(
0.
U
(
log2Ceil
(
RoqSize
+
1
).
W
))
val
allowEnqueue
=
RegInit
(
true
.
B
)
val
enqPtrVec
=
VecInit
((
0
until
RenameWidth
).
map
(
i
=>
enqPtr
+
PopCount
(
io
.
enq
.
needAlloc
.
take
(
i
))))
...
...
@@ -274,10 +274,10 @@ class Roq(numWbPorts: Int) extends XSModule with HasCircularQueuePtrHelper {
// by default, let all exceptions be determined by dispatch.
// mergeVec(instrAddrMisaligned) := dpData(instrAddrMisaligned)
// mergeVec(instrAccessFault) := dpData(instrAccessFault)
// mergeVec(illegalInstr) := dpData(illegalInstr)
// mergeVec(instrPageFault) := dpData(instrPageFault)
val
mergeVec
=
WireInit
(
dpData
.
exceptionVec
)
// these exceptions are determined in execution units
mergeVec
(
illegalInstr
)
:=
wbData
.
exceptionVec
(
illegalInstr
)
mergeVec
(
breakPoint
)
:=
wbData
.
exceptionVec
(
breakPoint
)
mergeVec
(
loadAddrMisaligned
)
:=
wbData
.
exceptionVec
(
loadAddrMisaligned
)
mergeVec
(
loadAccessFault
)
:=
wbData
.
exceptionVec
(
loadAccessFault
)
...
...
@@ -305,7 +305,7 @@ class Roq(numWbPorts: Int) extends XSModule with HasCircularQueuePtrHelper {
// To reduce registers usage, for hasBlockBackward cases, we allow enqueue after ROB is empty.
when
(
isEmpty
)
{
hasBlockBackward
:=
false
.
B
}
// When any instruction commits, hasNoSpecExec should be set to false.B
when
(
io
.
commits
.
valid
.
asUInt
.
orR
)
{
hasNoSpecExec
:=
false
.
B
}
when
(
io
.
commits
.
valid
.
asUInt
.
orR
&&
state
=/=
s_extrawalk
)
{
hasNoSpecExec
:=
false
.
B
}
io
.
enq
.
canAccept
:=
allowEnqueue
&&
!
hasBlockBackward
io
.
enq
.
isEmpty
:=
isEmpty
...
...
@@ -361,17 +361,16 @@ class Roq(numWbPorts: Int) extends XSModule with HasCircularQueuePtrHelper {
val
deqWritebackData
=
writebackDataRead
(
0
)
val
debug_deqUop
=
debug_microOp
(
deqPtr
.
value
)
val
deqPtrWritebacked
=
writebacked
(
deqPtr
.
value
)
&&
valid
(
deqPtr
.
value
)
val
deqExceptionVec
=
mergeExceptionVec
(
deqDispatchData
,
deqWritebackData
)
// For MMIO instructions, they should not trigger interrupts since they may be sent to lower level before it writes back.
// However, we cannot determine whether a load/store instruction is MMIO.
// Thus, we don't allow load/store instructions to trigger an interrupt.
val
intrBitSetReg
=
RegNext
(
io
.
csr
.
intrBitSet
)
val
intrEnable
=
intrBitSetReg
&&
valid
(
deqPtr
.
value
)
&&
!
hasNoSpecExec
&&
!
CommitType
.
isLoadStore
(
deqDispatchData
.
commitType
)
val
exceptionEnable
=
deqPtrWritebacked
&&
Cat
(
deqExceptionVec
).
orR
()
val
isFlushPipe
=
deqPtrWritebacked
&&
deqWritebackData
.
flushPipe
val
intrEnable
=
intrBitSetReg
&&
!
hasNoSpecExec
&&
!
CommitType
.
isLoadStore
(
deqDispatchData
.
commitType
)
val
exceptionEnable
=
writebacked
(
deqPtr
.
value
)
&&
Cat
(
deqExceptionVec
).
orR
()
val
isFlushPipe
=
writebacked
(
deqPtr
.
value
)
&&
deqWritebackData
.
flushPipe
io
.
redirectOut
:=
DontCare
io
.
redirectOut
.
valid
:=
(
state
===
s_idle
)
&&
(
intrEnable
||
exceptionEnable
||
isFlushPipe
)
io
.
redirectOut
.
valid
:=
(
state
===
s_idle
)
&&
valid
(
deqPtr
.
value
)
&&
(
intrEnable
||
exceptionEnable
||
isFlushPipe
)
io
.
redirectOut
.
bits
.
level
:=
Mux
(
isFlushPipe
,
RedirectLevel
.
flushAll
,
RedirectLevel
.
exception
)
io
.
redirectOut
.
bits
.
interrupt
:=
intrEnable
io
.
redirectOut
.
bits
.
target
:=
Mux
(
isFlushPipe
,
deqDispatchData
.
pc
+
4.
U
,
io
.
csr
.
trapTarget
)
...
...
@@ -414,7 +413,8 @@ class Roq(numWbPorts: Int) extends XSModule with HasCircularQueuePtrHelper {
val
commit_block
=
VecInit
((
0
until
CommitWidth
).
map
(
i
=>
!
commit_w
(
i
)
||
commit_exception
(
i
)
||
writebackDataRead
(
i
).
flushPipe
))
for
(
i
<-
0
until
CommitWidth
)
{
// defaults: state === s_idle and instructions commit
val
isBlocked
=
if
(
i
!=
0
)
Cat
(
commit_block
.
take
(
i
)).
orR
||
intrEnable
else
false
.
B
// when intrBitSetReg, allow only one instruction to commit at each clock cycle
val
isBlocked
=
if
(
i
!=
0
)
Cat
(
commit_block
.
take
(
i
)).
orR
||
intrBitSetReg
else
intrEnable
io
.
commits
.
valid
(
i
)
:=
commit_v
(
i
)
&&
commit_w
(
i
)
&&
!
isBlocked
&&
!
commit_exception
(
i
)
io
.
commits
.
info
(
i
)
:=
dispatchDataRead
(
i
)
...
...
src/main/scala/xiangshan/cache/icache.scala
浏览文件 @
4fcc0784
...
...
@@ -116,7 +116,7 @@ trait ICacheBase extends HasICacheParameters
//----------------------------
// Stage 1
//----------------------------
val
s1_valid
=
WireInit
(
false
.
B
)
//
val s1_valid = WireInit(false.B)
val
s1_req_pc
=
Wire
(
UInt
(
VAddrBits
.
W
))
val
s1_req_mask
=
Wire
(
UInt
(
PredictWidth
.
W
))
val
s1_fire
=
WireInit
(
false
.
B
)
...
...
@@ -267,6 +267,8 @@ class ICache extends ICacheModule
val
io
=
IO
(
new
ICacheIO
)
val
s2_flush
=
io
.
flush
(
0
)
val
s3_flush
=
io
.
flush
(
1
)
//----------------------------
// Memory Part
//----------------------------
...
...
@@ -278,21 +280,21 @@ class ICache extends ICacheModule
//----------------------------
// Stage 1
//----------------------------
s1_
valid
:=
io
.
req
.
fire
()
s1_
fire
:=
io
.
req
.
valid
s1_req_pc
:=
io
.
req
.
bits
.
addr
s1_req_mask
:=
io
.
req
.
bits
.
mask
s2_ready
:=
WireInit
(
false
.
B
)
s1_fire
:=
s1_valid
&&
(
s2_ready
||
io
.
flush
(
0
)
)
// s1_fire := s1_valid && (s2_ready || s2_flush
)
// SRAM(Meta and Data) read request
val
s1_idx
=
get_idx
(
s1_req_pc
)
metaArray
.
io
.
read
.
valid
:=
s1_
valid
metaArray
.
io
.
read
.
valid
:=
s1_
fire
metaArray
.
io
.
read
.
bits
:=
s1_idx
dataArray
.
io
.
read
.
valid
:=
s1_
valid
dataArray
.
io
.
read
.
valid
:=
s1_
fire
dataArray
.
io
.
read
.
bits
:=
s1_idx
XSDebug
(
"[Stage 1]
v : r : f (%d %d %d) request pc: 0x%x mask: %b\n"
,
s1_valid
,
s2_ready
,
s1_fire
,
s1_req_pc
,
s1_req_mask
)
XSDebug
(
"[Stage 1]
r : f (%d %d) request pc: 0x%x mask: %b\n"
,
s2_ready
,
s1_fire
,
s1_req_pc
,
s1_req_mask
)
XSDebug
(
"[Stage 1] index: %d\n"
,
s1_idx
)
...
...
@@ -304,10 +306,11 @@ class ICache extends ICacheModule
val
s2_tag
=
get_tag
(
s2_tlb_resp
.
paddr
)
val
s2_hit
=
WireInit
(
false
.
B
)
val
s2_access_fault
=
WireInit
(
false
.
B
)
s2_fire
:=
s2_valid
&&
s3_ready
&&
!
io
.
flush
(
0
)
&&
io
.
tlb
.
resp
.
fire
()
when
(
io
.
flush
(
0
))
{
s2_valid
:=
s1_fire
}
.
elsewhen
(
s1_fire
)
{
s2_valid
:=
s1_valid
}
.
elsewhen
(
s2_fire
)
{
s2_valid
:=
false
.
B
}
val
s2_allValid
=
s2_valid
&&
io
.
tlb
.
resp
.
valid
s2_fire
:=
s2_allValid
&&
s3_ready
when
(
s1_fire
)
{
s2_valid
:=
true
.
B
}
.
elsewhen
(
s2_flush
)
{
s2_valid
:=
false
.
B
}
.
elsewhen
(
s2_fire
)
{
s2_valid
:=
false
.
B
}
//physical address < 0x80000000
//TODO: May have bugs
...
...
@@ -329,7 +332,7 @@ class ICache extends ICacheModule
val
waymask
=
Mux
(
s2_hit
,
hitVec
.
asUInt
,
Mux
(
hasInvalidWay
,
refillInvalidWaymask
,
victimWayMask
))
s2_hit
:=
ParallelOR
(
hitVec
)
||
s2_tlb_resp
.
excp
.
pf
.
instr
||
s2_access_fault
s2_ready
:=
s
2_fire
||
!
s2_valid
||
io
.
flush
(
0
)
s2_ready
:=
s
3_ready
&&
io
.
tlb
.
resp
.
valid
||
!
s2_valid
XSDebug
(
"[Stage 2] v : r : f (%d %d %d) pc: 0x%x mask: %b acf:%d\n"
,
s2_valid
,
s3_ready
,
s2_fire
,
s2_req_pc
,
s2_req_mask
,
s2_access_fault
)
XSDebug
(
p
"[Stage 2] tlb req: v ${io.tlb.req.valid} r ${io.tlb.req.ready} ${io.tlb.req.bits}\n"
)
...
...
@@ -349,9 +352,9 @@ class ICache extends ICacheModule
val
s3_miss
=
s3_valid
&&
!
s3_hit
val
s3_idx
=
get_idx
(
s3_req_pc
)
val
s3_access_fault
=
RegEnable
(
s2_access_fault
,
init
=
false
.
B
,
enable
=
s2_fire
)
when
(
io
.
flush
(
1
))
{
s3_valid
:=
false
.
B
}
.
elsewhen
(
s2_fire
)
{
s3_valid
:=
s2_valid
}
.
elsewhen
(
io
.
resp
.
fire
())
{
s3_valid
:=
false
.
B
}
when
(
s3_flush
)
{
s3_valid
:=
false
.
B
}
.
elsewhen
(
s2_fire
&&
!
s2_flush
)
{
s3_valid
:=
true
.
B
}
.
elsewhen
(
io
.
resp
.
fire
())
{
s3_valid
:=
false
.
B
}
val
refillDataReg
=
Reg
(
Vec
(
refillCycles
,
UInt
(
beatBits
.
W
)))
// icache hit
...
...
@@ -373,15 +376,15 @@ class ICache extends ICacheModule
val
icacheMissQueue
=
Module
(
new
IcacheMissQueue
)
val
blocking
=
RegInit
(
false
.
B
)
val
isICacheResp
=
icacheMissQueue
.
io
.
resp
.
valid
&&
icacheMissQueue
.
io
.
resp
.
bits
.
clientID
===
cacheID
.
U
(
2.
W
)
icacheMissQueue
.
io
.
req
.
valid
:=
s3_miss
&&
!
io
.
flush
(
1
)
&&
!
blocking
//TODO: specificate flush condition
icacheMissQueue
.
io
.
req
.
valid
:=
s3_miss
&&
!
s3_flush
&&
!
blocking
//TODO: specificate flush condition
icacheMissQueue
.
io
.
req
.
bits
.
apply
(
missAddr
=
groupPC
(
s3_tlb_resp
.
paddr
),
missIdx
=
s3_idx
,
missWaymask
=
s3_wayMask
,
source
=
cacheID
.
U
(
2.
W
))
icacheMissQueue
.
io
.
resp
.
ready
:=
io
.
resp
.
ready
icacheMissQueue
.
io
.
flush
:=
io
.
flush
(
1
)
icacheMissQueue
.
io
.
flush
:=
s3_flush
when
(
icacheMissQueue
.
io
.
req
.
fire
()){
blocking
:=
true
.
B
}
.
elsewhen
(
blocking
&&
((
icacheMissQueue
.
io
.
resp
.
fire
()
&&
isICacheResp
)
||
io
.
flush
(
1
)
)
){
blocking
:=
false
.
B
}
.
elsewhen
(
blocking
&&
((
icacheMissQueue
.
io
.
resp
.
fire
()
&&
isICacheResp
)
||
s3_flush
)
){
blocking
:=
false
.
B
}
XSDebug
(
blocking
&&
io
.
flush
(
1
)
,
"check for icache non-blocking"
)
XSDebug
(
blocking
&&
s3_flush
,
"check for icache non-blocking"
)
//cache flush register
val
icacheFlush
=
io
.
fencei
val
cacheflushed
=
RegInit
(
false
.
B
)
...
...
@@ -419,7 +422,7 @@ class ICache extends ICacheModule
val
refillDataVec
=
icacheMissQueue
.
io
.
resp
.
bits
.
data
.
asTypeOf
(
Vec
(
blockWords
,
UInt
(
wordBits
.
W
)))
val
refillDataOut
=
cutHelper
(
refillDataVec
,
s3_req_pc
(
5
,
1
),
s3_req_mask
)
s3_ready
:=
((
io
.
resp
.
fire
()
||
!
s3_valid
)
&&
!
blocking
)
||
(
blocking
&&
icacheMissQueue
.
io
.
resp
.
fire
()
)
s3_ready
:=
((
io
.
resp
.
ready
&&
s3_hit
||
!
s3_valid
)
&&
!
blocking
)
||
(
blocking
&&
icacheMissQueue
.
io
.
resp
.
valid
&&
io
.
resp
.
ready
)
//TODO: coherence
XSDebug
(
"[Stage 3] valid:%d pc: 0x%x mask: %b ipf:%d acf:%d \n"
,
s3_valid
,
s3_req_pc
,
s3_req_mask
,
s3_tlb_resp
.
excp
.
pf
.
instr
,
s3_access_fault
)
...
...
@@ -439,7 +442,7 @@ class ICache extends ICacheModule
// Out Put
//----------------------------
//icache request
io
.
req
.
ready
:=
metaArray
.
io
.
read
.
ready
&&
dataArray
.
io
.
read
.
ready
&&
s2_ready
io
.
req
.
ready
:=
s2_ready
//icache response: to pre-decoder
io
.
resp
.
valid
:=
s3_valid
&&
(
s3_hit
||
icacheMissQueue
.
io
.
resp
.
valid
)
...
...
@@ -450,7 +453,7 @@ class ICache extends ICacheModule
io
.
resp
.
bits
.
acf
:=
s3_access_fault
//to itlb
io
.
tlb
.
resp
.
ready
:=
s3_ready
io
.
tlb
.
resp
.
ready
:=
true
.
B
// DontCare
io
.
tlb
.
req
.
valid
:=
s2_valid
io
.
tlb
.
req
.
bits
.
vaddr
:=
s2_req_pc
io
.
tlb
.
req
.
bits
.
cmd
:=
TlbCmd
.
exec
...
...
@@ -463,7 +466,7 @@ class ICache extends ICacheModule
io
.
l1plusflush
:=
icacheFlush
XSDebug
(
"[flush] flush_0:%d flush_1:%d\n"
,
io
.
flush
(
0
),
io
.
flush
(
1
)
)
XSDebug
(
"[flush] flush_0:%d flush_1:%d\n"
,
s2_flush
,
s3_flush
)
//Performance Counter
if
(!
env
.
FPGAPlatform
)
{
...
...
src/main/scala/xiangshan/cache/ptw.scala
浏览文件 @
4fcc0784
...
...
@@ -501,11 +501,11 @@ class PTWImp(outer: PTW) extends PtwModule(outer){
}
.
otherwise
{
when
(
sfence
.
bits
.
rs2
)
{
// specific leaf of addr && all asid
tlbv
:=
tlbv
&
~
UIntToOH
(
sfence
.
bits
.
addr
(
log2Up
(
TlbL2EntrySize
)-
1
+
offLen
,
0
+
offLen
))
tlbg
:=
tlbg
&
~
UIntToOH
(
sfence
.
bits
.
addr
(
log2Up
(
TlbL2EntrySize
)-
1
+
offLen
,
0
+
offLen
))
tlbv
:=
tlbv
&
~
UIntToOH
(
genTlbL2Idx
(
sfence
.
bits
.
addr
(
sfence
.
bits
.
addr
.
getWidth
-
1
,
offLen
)
))
tlbg
:=
tlbg
&
~
UIntToOH
(
genTlbL2Idx
(
sfence
.
bits
.
addr
(
sfence
.
bits
.
addr
.
getWidth
-
1
,
offLen
)
))
}
.
otherwise
{
// specific leaf of addr && specific asid
tlbv
:=
tlbv
&
(~
UIntToOH
(
sfence
.
bits
.
addr
(
log2Up
(
TlbL2EntrySize
)-
1
+
offLen
,
0
+
offLen
))
|
tlbg
)
tlbv
:=
tlbv
&
(~
UIntToOH
(
genTlbL2Idx
(
sfence
.
bits
.
addr
(
sfence
.
bits
.
addr
.
getWidth
-
1
,
offLen
)))
|
tlbg
)
}
}
}
...
...
src/main/scala/xiangshan/frontend/BPU.scala
浏览文件 @
4fcc0784
...
...
@@ -267,8 +267,8 @@ class BPUStage3 extends BPUStage {
val
pdLastHalf
=
s3IO
.
predecode
.
lastHalf
val
pds
=
s3IO
.
predecode
.
pd
val
btbResp
=
inLatch
.
resp
.
btb
val
btbHits
=
btbResp
.
hits
.
asUInt
val
btbResp
=
WireInit
(
inLatch
.
resp
.
btb
)
val
btbHits
=
WireInit
(
btbResp
.
hits
.
asUInt
)
val
bimTakens
=
VecInit
(
inLatch
.
resp
.
bim
.
ctrs
.
map
(
_
(
1
)))
val
brs
=
pdMask
&
Reverse
(
Cat
(
pds
.
map
(
_
.
isBr
)))
...
...
@@ -287,6 +287,8 @@ class BPUStage3 extends BPUStage {
val
prevHalfTakenMask
=
prevHalfTaken
.
asUInt
val
brTakens
=
((
brs
&
brPred
|
prevHalfTakenMask
)
&
~
loopRes
)
// VecInit((0 until PredictWidth).map(i => brs(i) && (brPred(i) || (if (i == 0) prevHalfTaken else false.B)) && !loopRes(i)))
// we should provide btb resp as well
btbHits
:=
btbResp
.
hits
.
asUInt
|
prevHalfTakenMask
// predict taken only if btb has a target, jal targets will be provided by IFU
takens
:=
VecInit
((
0
until
PredictWidth
).
map
(
i
=>
(
brTakens
(
i
)
||
jalrs
(
i
))
&&
btbHits
(
i
)
||
jals
(
i
)))
...
...
src/main/scala/xiangshan/frontend/Bim.scala
浏览文件 @
4fcc0784
...
...
@@ -108,9 +108,9 @@ class BIM extends BasePredictor with BimParams {
when
(
needToUpdate
)
{
when
(
wrbypass_hit
)
{
wrbypass_ctrs
(
wrbypass_hit_idx
)(
updateBank
)
:=
newCtr
wrbypass_ctr_valids
(
wrbypass_
enq
_idx
)(
updateBank
)
:=
true
.
B
wrbypass_ctr_valids
(
wrbypass_
hit
_idx
)(
updateBank
)
:=
true
.
B
}
.
otherwise
{
wrbypass_ctrs
(
wrbypass_
hit
_idx
)(
updateBank
)
:=
newCtr
wrbypass_ctrs
(
wrbypass_
enq
_idx
)(
updateBank
)
:=
newCtr
(
0
until
BimBanks
).
foreach
(
b
=>
wrbypass_ctr_valids
(
wrbypass_enq_idx
)(
b
)
:=
false
.
B
)
// reset valid bits
wrbypass_ctr_valids
(
wrbypass_enq_idx
)(
updateBank
)
:=
true
.
B
wrbypass_rows
(
wrbypass_enq_idx
)
:=
updateRow
...
...
src/main/scala/xiangshan/frontend/Frontend.scala
浏览文件 @
4fcc0784
...
...
@@ -21,35 +21,29 @@ class Frontend extends XSModule {
val
ifu
=
Module
(
new
IFU
)
val
ibuffer
=
Module
(
new
Ibuffer
)
val
icache
=
Module
(
new
ICache
)
val
needFlush
=
io
.
backend
.
redirect
.
valid
//backend
//
from
backend
ifu
.
io
.
redirect
<>
io
.
backend
.
redirect
ifu
.
io
.
cfiUpdateInfo
<>
io
.
backend
.
cfiUpdateInfo
// ifu.io.cfiUpdateInfo <> io.backend.cfiUpdateInfo
//icache
ifu
.
io
.
icacheResp
<>
icache
.
io
.
resp
icache
.
io
.
req
<>
ifu
.
io
.
icacheReq
icache
.
io
.
flush
<>
ifu
.
io
.
icacheFlush
icache
.
io
.
fencei
:=
io
.
fencei
io
.
l1plusFlush
:=
icache
.
io
.
l1plusflush
io
.
icacheMemAcq
<>
icache
.
io
.
mem_acquire
icache
.
io
.
mem_grant
<>
io
.
icacheMemGrant
//itlb to ptw
io
.
ptw
<>
TLB
(
in
=
Seq
(
icache
.
io
.
tlb
),
sfence
=
io
.
sfence
,
csr
=
io
.
tlbCsr
,
width
=
1
,
isDtlb
=
false
,
shouldBlock
=
true
)
//ibuffer
// to icache
ifu
.
io
.
icacheMemGrant
<>
io
.
icacheMemGrant
ifu
.
io
.
fencei
:=
io
.
fencei
// to tlb
ifu
.
io
.
sfence
:=
io
.
sfence
ifu
.
io
.
tlbCsr
:=
io
.
tlbCsr
// from icache
io
.
l1plusFlush
:=
ifu
.
io
.
l1plusFlush
io
.
icacheMemAcq
<>
ifu
.
io
.
icacheMemAcq
// itlb to ptw
io
.
ptw
<>
ifu
.
io
.
ptw
// ifu to ibuffer
ibuffer
.
io
.
in
<>
ifu
.
io
.
fetchPacket
// backend to ibuffer
ibuffer
.
io
.
flush
:=
needFlush
// ibuffer to backend
io
.
backend
.
cfVec
<>
ibuffer
.
io
.
out
// for(out <- ibuffer.io.out){
...
...
src/main/scala/xiangshan/frontend/IFU.scala
浏览文件 @
4fcc0784
...
...
@@ -28,13 +28,9 @@ trait HasIFUConst extends HasXSParameter {
// each 1 bit in mask stands for 2 Bytes
// 8 bits, in which only the first 7 bits could be 0
def
maskFirstHalf
(
pc
:
UInt
)
:
UInt
=
((~(
0.
U
(
bankWidth
.
W
)))
>>
offsetInBank
(
pc
))(
bankWidth
-
1
,
0
)
// when in loop(buffer), we need to make use of the full packet
// and get the real mask in iCacheResp from loop buffer
// we may make predictions on more instructions than we could get from loop buffer
// and this will be handled in if4
def
maskLastHalf
(
pc
:
UInt
,
inLoop
:
Bool
=
false
.
B
)
:
UInt
=
Mux
(
isInLastBank
(
pc
)
&&
!
inLoop
,
0.
U
(
bankWidth
.
W
),
~
0.
U
(
bankWidth
.
W
))
def
mask
(
pc
:
UInt
,
inLoop
:
Bool
=
false
.
B
)
:
UInt
=
Reverse
(
Cat
(
maskFirstHalf
(
pc
),
maskLastHalf
(
pc
,
inLoop
)))
def
snpc
(
pc
:
UInt
,
inLoop
:
Bool
=
false
.
B
)
:
UInt
=
pc
+
(
PopCount
(
mask
(
pc
,
inLoop
))
<<
1
)
def
maskLastHalf
(
pc
:
UInt
)
:
UInt
=
Mux
(
isInLastBank
(
pc
),
0.
U
(
bankWidth
.
W
),
~
0.
U
(
bankWidth
.
W
))
def
mask
(
pc
:
UInt
)
:
UInt
=
Reverse
(
Cat
(
maskFirstHalf
(
pc
),
maskLastHalf
(
pc
)))
def
snpc
(
pc
:
UInt
)
:
UInt
=
bankAligned
(
pc
)
+
Mux
(
isInLastBank
(
pc
),
bankBytes
.
U
,
(
bankBytes
*
2
).
U
)
val
enableGhistRepair
=
true
val
IFUDebug
=
true
...
...
@@ -68,14 +64,22 @@ class GlobalHistory extends XSBundle {
class
IFUIO
extends
XSBundle
{
// to ibuffer
val
fetchPacket
=
DecoupledIO
(
new
FetchPacket
)
// from backend
val
redirect
=
Flipped
(
ValidIO
(
UInt
(
VAddrBits
.
W
)))
// val cfiUpdateInfo = Flipped(ValidIO(new CfiUpdateInfo))
val
cfiUpdateInfo
=
Flipped
(
ValidIO
(
new
CfiUpdateInfo
))
val
icacheReq
=
DecoupledIO
(
new
ICacheReq
)
val
icacheResp
=
Flipped
(
DecoupledIO
(
new
ICacheResp
))
val
icacheFlush
=
Output
(
UInt
(
2.
W
))
// val loopBufPar = Flipped(new LoopBufferParameters)
// to icache
val
icacheMemGrant
=
Flipped
(
DecoupledIO
(
new
L1plusCacheResp
))
val
fencei
=
Input
(
Bool
())
// from icache
val
icacheMemAcq
=
DecoupledIO
(
new
L1plusCacheReq
)
val
l1plusFlush
=
Output
(
Bool
())
// to tlb
val
sfence
=
Input
(
new
SfenceBundle
)
val
tlbCsr
=
Input
(
new
TlbCsrBundle
)
// from tlb
val
ptw
=
new
TlbPtwIO
}
class
PrevHalfInstr
extends
XSBundle
{
...
...
@@ -88,7 +92,8 @@ class PrevHalfInstr extends XSBundle {
val
target
=
UInt
(
VAddrBits
.
W
)
val
instr
=
UInt
(
16.
W
)
val
ipf
=
Bool
()
val
newPtr
=
UInt
(
log2Up
(
ExtHistoryLength
).
W
)
val
meta
=
new
BpuMeta
// val newPtr = UInt(log2Up(ExtHistoryLength).W)
}
@chiselName
...
...
@@ -96,29 +101,34 @@ class IFU extends XSModule with HasIFUConst
{
val
io
=
IO
(
new
IFUIO
)
val
bpu
=
BPU
(
EnableBPU
)
val
icache
=
Module
(
new
ICache
)
val
pd
=
Module
(
new
PreDecode
)
val
loopBuffer
=
if
(
EnableLB
)
{
Module
(
new
LoopBuffer
)
}
else
{
Module
(
new
FakeLoopBuffer
)
}
io
.
ptw
<>
TLB
(
in
=
Seq
(
icache
.
io
.
tlb
),
sfence
=
io
.
sfence
,
csr
=
io
.
tlbCsr
,
width
=
1
,
isDtlb
=
false
,
shouldBlock
=
true
)
val
if2_redirect
,
if3_redirect
,
if4_redirect
=
WireInit
(
false
.
B
)
val
if1_flush
,
if2_flush
,
if3_flush
,
if4_flush
=
WireInit
(
false
.
B
)
val
loopBufPar
=
loopBuffer
.
io
.
loopBufPar
val
inLoop
=
WireInit
(
loopBuffer
.
io
.
out
.
valid
)
val
icacheResp
=
WireInit
(
Mux
(
inLoop
,
loopBuffer
.
io
.
out
.
bits
,
io
.
icacheResp
.
bits
))
val
icacheResp
=
icache
.
io
.
resp
.
bits
if4_flush
:=
io
.
redirect
.
valid
||
loopBufPar
.
LBredirect
.
valid
if4_flush
:=
io
.
redirect
.
valid
if3_flush
:=
if4_flush
||
if4_redirect
if2_flush
:=
if3_flush
||
if3_redirect
if1_flush
:=
if2_flush
||
if2_redirect
loopBuffer
.
io
.
flush
:=
io
.
redirect
.
valid
//********************** IF1 ****************************//
val
if1_valid
=
!
reset
.
asBool
&&
GTimer
()
>
500.
U
val
if1_npc
=
WireInit
(
0.
U
(
VAddrBits
.
W
))
val
if2_ready
=
WireInit
(
false
.
B
)
val
if2_allReady
=
WireInit
(
if2_ready
&&
(
inLoop
||
io
.
icacheReq
.
ready
)
)
val
if1_fire
=
if1_valid
&&
(
if2_allReady
||
if
1
_flush
)
val
if2_allReady
=
WireInit
(
if2_ready
&&
icache
.
io
.
req
.
ready
)
val
if1_fire
=
if1_valid
&&
(
if2_allReady
||
if
2
_flush
)
// val if2_newPtr, if3_newPtr, if4_newPtr = Wire(UInt(log2Up(ExtHistoryLength).W))
...
...
@@ -131,12 +141,13 @@ class IFU extends XSModule with HasIFUConst
//********************** IF2 ****************************//
val
if2_valid
=
RegInit
(
init
=
false
.
B
)
val
if2_allValid
=
if2_valid
&&
icache
.
io
.
tlb
.
resp
.
valid
val
if3_ready
=
WireInit
(
false
.
B
)
val
if2_fire
=
if2_
v
alid
&&
if3_ready
val
if2_fire
=
if2_
allV
alid
&&
if3_ready
val
if2_pc
=
RegEnable
(
next
=
if1_npc
,
init
=
resetVector
.
U
,
enable
=
if1_fire
)
val
if2_snpc
=
snpc
(
if2_pc
,
inLoop
)
val
if2_snpc
=
snpc
(
if2_pc
)
val
if2_predHist
=
RegEnable
(
if1_gh
.
predHist
,
enable
=
if1_fire
)
if2_ready
:=
if3_ready
||
!
if2_valid
if2_ready
:=
if3_ready
&&
icache
.
io
.
tlb
.
resp
.
valid
||
!
if2_valid
when
(
if1_fire
)
{
if2_valid
:=
true
.
B
}
.
elsewhen
(
if2_flush
)
{
if2_valid
:=
false
.
B
}
.
elsewhen
(
if2_fire
)
{
if2_valid
:=
false
.
B
}
...
...
@@ -157,11 +168,11 @@ class IFU extends XSModule with HasIFUConst
// if3 should wait for instructions resp to arrive
val
if3_valid
=
RegInit
(
init
=
false
.
B
)
val
if4_ready
=
WireInit
(
false
.
B
)
val
if3_allValid
=
if3_valid
&&
(
inLoop
||
io
.
icacheResp
.
valid
)
val
if3_allValid
=
if3_valid
&&
icache
.
io
.
resp
.
valid
val
if3_fire
=
if3_allValid
&&
if4_ready
val
if3_pc
=
RegEnable
(
if2_pc
,
if2_fire
)
val
if3_predHist
=
RegEnable
(
if2_predHist
,
enable
=
if2_fire
)
if3_ready
:=
if4_ready
&&
(
inLoop
||
io
.
icacheResp
.
valid
)
||
!
if3_valid
if3_ready
:=
if4_ready
&&
icache
.
io
.
resp
.
valid
||
!
if3_valid
when
(
if3_flush
)
{
if3_valid
:=
false
.
B
}.
elsewhen
(
if2_fire
&&
!
if2_flush
)
{
...
...
@@ -190,9 +201,11 @@ class IFU extends XSModule with HasIFUConst
// set to invalid once consumed or redirect from backend
val
if3_prevHalfConsumed
=
if3_prevHalfInstrMet
&&
if3_fire
val
if3_prevHalfFlush
=
if4_flush
when
(
hasPrevHalfInstrReq
&&
!
if3_prevHalfFlush
)
{
when
(
if3_prevHalfFlush
)
{
if3_prevHalfInstr
.
valid
:=
false
.
B
}.
elsewhen
(
hasPrevHalfInstrReq
)
{
if3_prevHalfInstr
.
valid
:=
true
.
B
}.
elsewhen
(
if3_prevHalfConsumed
||
if3_prevHalfFlush
)
{
}.
elsewhen
(
if3_prevHalfConsumed
)
{
if3_prevHalfInstr
.
valid
:=
false
.
B
}
when
(
hasPrevHalfInstrReq
)
{
...
...
@@ -205,7 +218,7 @@ class IFU extends XSModule with HasIFUConst
val
if3_prevHalfMetRedirect
=
if3_pendingPrevHalfInstr
&&
if3_prevHalfInstrMet
&&
if3_prevHalfInstr
.
bits
.
taken
&&
if3_nextValidPCNotEquals
(
if3_prevHalfInstr
.
bits
.
target
)
val
if3_prevHalfNotMetRedirect
=
if3_pendingPrevHalfInstr
&&
!
if3_prevHalfInstrMet
&&
if3_nextValidPCNotEquals
(
if3_prevHalfInstr
.
bits
.
npc
)
val
if3_predTakenRedirect
=
!
if3_pendingPrevHalfInstr
&&
if3_bp
.
taken
&&
if3_nextValidPCNotEquals
(
if3_bp
.
target
)
val
if3_predNotTakenRedirect
=
!
if3_pendingPrevHalfInstr
&&
!
if3_bp
.
taken
&&
if3_nextValidPCNotEquals
(
snpc
(
if3_pc
,
inLoop
))
val
if3_predNotTakenRedirect
=
!
if3_pendingPrevHalfInstr
&&
!
if3_bp
.
taken
&&
if3_nextValidPCNotEquals
(
snpc
(
if3_pc
))
// when pendingPrevHalfInstr, if3_GHInfo is set to the info of last prev half instr
// val if3_ghInfoNotIdenticalRedirect = !if3_pendingPrevHalfInstr && if3_GHInfo =/= if3_lastGHInfo && enableGhistRepair.B
...
...
@@ -252,9 +265,9 @@ class IFU extends XSModule with HasIFUConst
val
if4_valid
=
RegInit
(
false
.
B
)
val
if4_fire
=
if4_valid
&&
io
.
fetchPacket
.
ready
val
if4_pc
=
RegEnable
(
if3_pc
,
if3_fire
)
// This is the real mask given from icache
or loop buffer
// This is the real mask given from icache
val
if4_mask
=
RegEnable
(
icacheResp
.
mask
,
if3_fire
)
val
if4_snpc
=
Mux
(
inLoop
,
if4_pc
+
(
PopCount
(
if4_mask
)
<<
1
),
snpc
(
if4_pc
)
)
val
if4_snpc
=
snpc
(
if4_pc
)
val
if4_predHist
=
RegEnable
(
if3_predHist
,
enable
=
if3_fire
)
...
...
@@ -270,9 +283,9 @@ class IFU extends XSModule with HasIFUConst
val
if4_bp
=
Wire
(
new
BranchPrediction
)
if4_bp
:=
bpu
.
io
.
out
(
2
)
if4_bp
.
takens
:=
bpu
.
io
.
out
(
2
).
takens
&
if4_mask
if4_bp
.
brMask
:=
bpu
.
io
.
out
(
2
).
brMask
&
if4_mask
if4_bp
.
jalMask
:=
bpu
.
io
.
out
(
2
).
jalMask
&
if4_mask
//
if4_bp.takens := bpu.io.out(2).takens & if4_mask
//
if4_bp.brMask := bpu.io.out(2).brMask & if4_mask
//
if4_bp.jalMask := bpu.io.out(2).jalMask & if4_mask
if4_predicted_gh
:=
if4_gh
.
update
(
if4_bp
.
hasNotTakenBrs
,
if4_bp
.
takenOnBr
)
...
...
@@ -301,9 +314,11 @@ class IFU extends XSModule with HasIFUConst
val
if4_prevHalfFlush
=
if4_flush
val
if4_takenPrevHalf
=
WireInit
(
if4_prevHalfInstrMet
&&
if4_prevHalfInstr
.
bits
.
taken
)
when
(
if3_prevHalfConsumed
)
{
when
(
if4_prevHalfFlush
)
{
if4_prevHalfInstr
.
valid
:=
false
.
B
}.
elsewhen
(
if3_prevHalfConsumed
)
{
if4_prevHalfInstr
.
valid
:=
if3_prevHalfInstr
.
valid
}.
elsewhen
(
if4_prevHalfConsumed
||
if4_prevHalfFlush
)
{
}.
elsewhen
(
if4_prevHalfConsumed
)
{
if4_prevHalfInstr
.
valid
:=
false
.
B
}
...
...
@@ -317,7 +332,6 @@ class IFU extends XSModule with HasIFUConst
// this is result of the last half RVI
prevHalfInstrReq
.
bits
.
taken
:=
if4_bp
.
lastHalfRVITaken
prevHalfInstrReq
.
bits
.
ghInfo
:=
if4_gh
prevHalfInstrReq
.
bits
.
newPtr
:=
DontCare
prevHalfInstrReq
.
bits
.
fetchpc
:=
if4_pc
prevHalfInstrReq
.
bits
.
idx
:=
idx
prevHalfInstrReq
.
bits
.
pc
:=
if4_pd
.
pc
(
idx
)
...
...
@@ -325,6 +339,7 @@ class IFU extends XSModule with HasIFUConst
prevHalfInstrReq
.
bits
.
target
:=
if4_bp
.
lastHalfRVITarget
prevHalfInstrReq
.
bits
.
instr
:=
if4_pd
.
instrs
(
idx
)(
15
,
0
)
prevHalfInstrReq
.
bits
.
ipf
:=
if4_ipf
prevHalfInstrReq
.
bits
.
meta
:=
bpu
.
io
.
bpuMeta
(
idx
)
def
if4_nextValidPCNotEquals
(
pc
:
UInt
)
=
if3_valid
&&
if3_pc
=/=
pc
||
!
if3_valid
&&
(
if2_valid
&&
if2_pc
=/=
pc
)
||
...
...
@@ -385,35 +400,21 @@ class IFU extends XSModule with HasIFUConst
flush_final_gh
:=
true
.
B
}
npcGen
.
register
(
loopBufPar
.
LBredirect
.
valid
,
loopBufPar
.
LBredirect
.
bits
)
npcGen
.
register
(
io
.
redirect
.
valid
,
io
.
redirect
.
bits
)
npcGen
.
register
(
RegNext
(
reset
.
asBool
)
&&
!
reset
.
asBool
,
resetVector
.
U
(
VAddrBits
.
W
))
if1_npc
:=
npcGen
()
when
(
inLoop
)
{
io
.
icacheReq
.
valid
:=
if4_flush
}.
otherwise
{
io
.
icacheReq
.
valid
:=
if1_valid
&&
(
if2_ready
||
if1_flush
)
}
io
.
icacheResp
.
ready
:=
if4_ready
io
.
icacheReq
.
bits
.
addr
:=
if1_npc
// when(if4_bp.taken) {
// when(if4_bp.saveHalfRVI) {
// io.loopBufPar.LBReq := snpc(if4_pc)
// }.otherwise {
// io.loopBufPar.LBReq := if4_bp.target
// }
// }.otherwise {
// io.loopBufPar.LBReq := snpc(if4_pc)
// XSDebug(p"snpc(if4_pc)=${Hexadecimal(snpc(if4_pc))}\n")
// }
loopBufPar
.
fetchReq
:=
if3_pc
io
.
icacheReq
.
bits
.
mask
:=
mask
(
if1_npc
)
io
.
icacheFlush
:=
Cat
(
if3_flush
,
if2_flush
)
icache
.
io
.
req
.
valid
:=
if1_valid
&&
(
if2_ready
||
if2_flush
)
icache
.
io
.
resp
.
ready
:=
if4_ready
icache
.
io
.
req
.
bits
.
addr
:=
if1_npc
icache
.
io
.
req
.
bits
.
mask
:=
mask
(
if1_npc
)
icache
.
io
.
flush
:=
Cat
(
if3_flush
,
if2_flush
)
icache
.
io
.
mem_grant
<>
io
.
icacheMemGrant
icache
.
io
.
fencei
:=
io
.
fencei
io
.
icacheMemAcq
<>
icache
.
io
.
mem_acquire
io
.
l1plusFlush
:=
icache
.
io
.
l1plusflush
bpu
.
io
.
cfiUpdateInfo
<>
io
.
cfiUpdateInfo
...
...
@@ -435,13 +436,6 @@ class IFU extends XSModule with HasIFUConst
bpu
.
io
.
prevHalf
:=
if4_prevHalfInstr
pd
.
io
.
in
:=
icacheResp
when
(
inLoop
)
{
pd
.
io
.
in
.
mask
:=
loopBuffer
.
io
.
out
.
bits
.
mask
// TODO: Maybe this is unnecessary
// XSDebug("Fetch from LB\n")
// XSDebug(p"pc=${Hexadecimal(io.loopBufPar.LBResp.pc)}\n")
// XSDebug(p"data=${Hexadecimal(io.loopBufPar.LBResp.data)}\n")
// XSDebug(p"mask=${Hexadecimal(io.loopBufPar.LBResp.mask)}\n")
}
pd
.
io
.
prev
.
valid
:=
if3_prevHalfInstrMet
pd
.
io
.
prev
.
bits
:=
if3_prevHalfInstr
.
bits
.
instr
...
...
@@ -459,12 +453,6 @@ class IFU extends XSModule with HasIFUConst
when
(
icacheResp
.
ipf
&&
!
if3_prevHalfInstr
.
bits
.
ipf
)
{
crossPageIPF
:=
true
.
B
}
// higher 16 bits page fault
}
//Performance Counter
// if (!env.FPGAPlatform ) {
// ExcitingUtils.addSource(io.fetchPacket.fire && !inLoop, "CntFetchFromICache", Perf)
// ExcitingUtils.addSource(io.fetchPacket.fire && inLoop, "CntFetchFromLoopBuffer", Perf)
// }
val
fetchPacketValid
=
if4_valid
&&
!
io
.
redirect
.
valid
val
fetchPacketWire
=
Wire
(
new
FetchPacket
)
...
...
@@ -473,13 +461,16 @@ class IFU extends XSModule with HasIFUConst
fetchPacketWire
.
mask
:=
if4_pd
.
mask
&
(
Fill
(
PredictWidth
,
!
if4_bp
.
taken
)
|
(
Fill
(
PredictWidth
,
1.
U
(
1.
W
))
>>
(~
if4_bp
.
jmpIdx
)))
fetchPacketWire
.
pdmask
:=
if4_pd
.
mask
loopBufPar
.
noTakenMask
:=
if4_pd
.
mask
fetchPacketWire
.
pc
:=
if4_pd
.
pc
(
0
until
PredictWidth
).
foreach
(
i
=>
fetchPacketWire
.
pnpc
(
i
)
:=
if4_pd
.
pc
(
i
)
+
Mux
(
if4_pd
.
pd
(
i
).
isRVC
,
2.
U
,
4.
U
))
when
(
if4_bp
.
taken
)
{
fetchPacketWire
.
pnpc
(
if4_bp
.
jmpIdx
)
:=
if4_bp
.
target
}
fetchPacketWire
.
bpuMeta
:=
bpu
.
io
.
bpuMeta
// save it for update
when
(
if4_pendingPrevHalfInstr
)
{
fetchPacketWire
.
bpuMeta
(
0
)
:=
if4_prevHalfInstr
.
bits
.
meta
}
(
0
until
PredictWidth
).
foreach
(
i
=>
{
val
meta
=
fetchPacketWire
.
bpuMeta
(
i
)
meta
.
hist
:=
final_gh
...
...
@@ -494,30 +485,28 @@ class IFU extends XSModule with HasIFUConst
// predTaken Vec
fetchPacketWire
.
predTaken
:=
if4_bp
.
taken
loopBuffer
.
io
.
in
.
bits
:=
fetchPacketWire
io
.
fetchPacket
.
bits
:=
fetchPacketWire
io
.
fetchPacket
.
valid
:=
fetchPacketValid
loopBuffer
.
io
.
in
.
valid
:=
io
.
fetchPacket
.
fire
// debug info
if
(
IFUDebug
)
{
XSDebug
(
RegNext
(
reset
.
asBool
)
&&
!
reset
.
asBool
,
"Reseting...\n"
)
XSDebug
(
i
o
.
icacheF
lush
(
0
).
asBool
,
"Flush icache stage2...\n"
)
XSDebug
(
i
o
.
icacheF
lush
(
1
).
asBool
,
"Flush icache stage3...\n"
)
XSDebug
(
i
cache
.
io
.
f
lush
(
0
).
asBool
,
"Flush icache stage2...\n"
)
XSDebug
(
i
cache
.
io
.
f
lush
(
1
).
asBool
,
"Flush icache stage3...\n"
)
XSDebug
(
io
.
redirect
.
valid
,
p
"Redirect from backend! target=${Hexadecimal(io.redirect.bits)}\n"
)
XSDebug
(
"[IF1] v=%d fire=%d flush=%d pc=%x mask=%b\n"
,
if1_valid
,
if1_fire
,
if1_flush
,
if1_npc
,
mask
(
if1_npc
))
XSDebug
(
"[IF2] v=%d r=%d fire=%d redirect=%d flush=%d pc=%x snpc=%x\n"
,
if2_valid
,
if2_ready
,
if2_fire
,
if2_redirect
,
if2_flush
,
if2_pc
,
if2_snpc
)
XSDebug
(
"[IF3] v=%d r=%d fire=%d redirect=%d flush=%d pc=%x crossPageIPF=%d sawNTBrs=%d\n"
,
if3_valid
,
if3_ready
,
if3_fire
,
if3_redirect
,
if3_flush
,
if3_pc
,
crossPageIPF
,
if3_bp
.
hasNotTakenBrs
)
XSDebug
(
"[IF4] v=%d r=%d fire=%d redirect=%d flush=%d pc=%x crossPageIPF=%d sawNTBrs=%d\n"
,
if4_valid
,
if4_ready
,
if4_fire
,
if4_redirect
,
if4_flush
,
if4_pc
,
if4_crossPageIPF
,
if4_bp
.
hasNotTakenBrs
)
XSDebug
(
"[IF1][icacheReq] v=%d r=%d addr=%x\n"
,
i
o
.
icacheReq
.
valid
,
io
.
icacheReq
.
ready
,
io
.
icacheR
eq
.
bits
.
addr
)
XSDebug
(
"[IF1][icacheReq] v=%d r=%d addr=%x\n"
,
i
cache
.
io
.
req
.
valid
,
icache
.
io
.
req
.
ready
,
icache
.
io
.
r
eq
.
bits
.
addr
)
XSDebug
(
"[IF1][ghr] hist=%b\n"
,
if1_gh
.
asUInt
)
XSDebug
(
"[IF1][ghr] extHist=%b\n\n"
,
if1_gh
.
asUInt
)
XSDebug
(
"[IF2][bp] taken=%d jmpIdx=%d hasNTBrs=%d target=%x saveHalfRVI=%d\n\n"
,
if2_bp
.
taken
,
if2_bp
.
jmpIdx
,
if2_bp
.
hasNotTakenBrs
,
if2_bp
.
target
,
if2_bp
.
saveHalfRVI
)
if2_gh
.
debug
(
"if2"
)
XSDebug
(
"[IF3][icacheResp] v=%d r=%d pc=%x mask=%b\n"
,
i
o
.
icacheResp
.
valid
,
io
.
icacheResp
.
ready
,
io
.
icacheResp
.
bits
.
pc
,
io
.
icacheR
esp
.
bits
.
mask
)
XSDebug
(
"[IF3][icacheResp] v=%d r=%d pc=%x mask=%b\n"
,
i
cache
.
io
.
resp
.
valid
,
icache
.
io
.
resp
.
ready
,
icache
.
io
.
resp
.
bits
.
pc
,
icache
.
io
.
r
esp
.
bits
.
mask
)
XSDebug
(
"[IF3][bp] taken=%d jmpIdx=%d hasNTBrs=%d target=%x saveHalfRVI=%d\n"
,
if3_bp
.
taken
,
if3_bp
.
jmpIdx
,
if3_bp
.
hasNotTakenBrs
,
if3_bp
.
target
,
if3_bp
.
saveHalfRVI
)
XSDebug
(
"[IF3][redirect]: v=%d, prevMet=%d, prevNMet=%d, predT=%d, predNT=%d\n"
,
if3_redirect
,
if3_prevHalfMetRedirect
,
if3_prevHalfNotMetRedirect
,
if3_predTakenRedirect
,
if3_predNotTakenRedirect
)
// XSDebug("[IF3][prevHalfInstr] v=%d redirect=%d fetchpc=%x idx=%d tgt=%x taken=%d instr=%x\n\n",
...
...
src/main/scala/xiangshan/frontend/Ibuffer.scala
浏览文件 @
4fcc0784
...
...
@@ -52,24 +52,33 @@ class Ibuffer extends XSModule with HasCircularQueuePtrHelper {
}
// Ibuffer define
val
ibuf
=
Mem
(
IBufSize
,
new
IBufEntry
)
val
ibuf
=
Reg
(
Vec
(
IBufSize
,
new
IBufEntry
)
)
val
head_ptr
=
RegInit
(
IbufPtr
(
false
.
B
,
0.
U
))
val
tail_ptr
=
RegInit
(
IbufPtr
(
false
.
B
,
0.
U
))
val
tail_vec
=
RegInit
(
VecInit
((
0
until
PredictWidth
).
map
(
_
.
U
.
asTypeOf
(
new
IbufPtr
))))
val
tail_ptr
=
tail_vec
(
0
)
val
validEntries
=
distanceBetween
(
tail_ptr
,
head_ptr
)
// valid entries
// val validEntries = distanceBetween(tail_ptr, head_ptr) // valid entries
val
validEntries
=
RegInit
(
0.
U
(
log2Up
(
IBufSize
+
1
).
W
))
// valid entries
val
allowEnq
=
RegInit
(
true
.
B
)
val
enqValid
=
IBufSize
.
U
-
PredictWidth
.
U
>=
validEntries
// val enqValid = (IBufSize.U - PredictWidth.U)
>= validEntries
val
deqValid
=
validEntries
>
0.
U
val
numEnq
=
Mux
(
io
.
in
.
fire
,
PopCount
(
io
.
in
.
bits
.
mask
),
0.
U
)
val
numDeq
=
Mux
(
deqValid
,
PopCount
(
io
.
out
.
map
(
_
.
fire
)),
0.
U
)
validEntries
:=
validEntries
+
numEnq
-
numDeq
allowEnq
:=
(
IBufSize
.
U
-
PredictWidth
.
U
)
>=
(
validEntries
+
numEnq
)
// Enque
io
.
in
.
ready
:=
enqValid
io
.
in
.
ready
:=
allowEnq
val
enq_vec
=
Wire
(
Vec
(
PredictWidth
,
UInt
(
log2Up
(
IBufSize
).
W
)))
val
offset
=
Wire
(
Vec
(
PredictWidth
,
UInt
(
log2Up
(
PredictWidth
).
W
)))
for
(
i
<-
0
until
PredictWidth
)
{
if
(
i
==
0
)
{
enq_vec
(
i
)
:=
tail_ptr
.
value
offset
(
i
)
:=
0.
U
}
else
{
enq_vec
(
i
)
:=
tail_ptr
.
value
+
PopCount
(
io
.
in
.
bits
.
pdmask
(
i
-
1
,
0
))
offset
(
i
)
:=
PopCount
(
io
.
in
.
bits
.
pdmask
(
i
-
1
,
0
))
}
}
...
...
@@ -87,11 +96,11 @@ class Ibuffer extends XSModule with HasCircularQueuePtrHelper {
inWire
.
ipf
:=
io
.
in
.
bits
.
ipf
inWire
.
acf
:=
io
.
in
.
bits
.
acf
inWire
.
crossPageIPFFix
:=
io
.
in
.
bits
.
crossPageIPFFix
ibuf
(
enq_vec
(
i
)
)
:=
inWire
ibuf
(
tail_vec
(
offset
(
i
)).
value
)
:=
inWire
}
}
tail_
ptr
:=
tail_ptr
+
PopCount
(
io
.
in
.
bits
.
mask
)
tail_
vec
:=
VecInit
(
tail_vec
.
map
(
_
+
PopCount
(
io
.
in
.
bits
.
mask
))
)
}
// Deque
...
...
@@ -125,10 +134,11 @@ class Ibuffer extends XSModule with HasCircularQueuePtrHelper {
// Flush
when
(
io
.
flush
)
{
validEntries
:=
0.
U
allowEnq
:=
true
.
B
head_ptr
.
value
:=
0.
U
head_ptr
.
flag
:=
false
.
B
tail_ptr
.
value
:=
0.
U
tail_ptr
.
flag
:=
false
.
B
tail_vec
:=
VecInit
((
0
until
PredictWidth
).
map
(
_
.
U
.
asTypeOf
(
new
IbufPtr
)))
}
// Debug info
...
...
@@ -164,7 +174,7 @@ class Ibuffer extends XSModule with HasCircularQueuePtrHelper {
// )
// }
XSDebug
(
p
"last_head_ptr=$head_ptr last_tail_ptr=$tail_ptr\n"
)
XSDebug
(
p
"
validEntries=$validEntries,
last_head_ptr=$head_ptr last_tail_ptr=$tail_ptr\n"
)
for
(
i
<-
0
until
IBufSize
/
8
)
{
XSDebug
(
"%x | %x | %x | %x | %x | %x | %x | %x\n"
,
ibuf
(
i
*
8
+
0
).
inst
,
...
...
src/main/scala/xiangshan/mem/lsqueue/LoadQueue.scala
浏览文件 @
4fcc0784
...
...
@@ -62,7 +62,7 @@ class LoadQueue extends XSModule with HasDCacheParameters with HasCircularQueueP
val
enqPtrExt
=
RegInit
(
VecInit
((
0
until
RenameWidth
).
map
(
_
.
U
.
asTypeOf
(
new
LqPtr
))))
val
deqPtrExt
=
RegInit
(
0.
U
.
asTypeOf
(
new
LqPtr
))
val
validCounter
=
RegInit
(
0.
U
(
log2Ceil
(
LoadQueueSize
).
W
))
val
validCounter
=
RegInit
(
0.
U
(
log2Ceil
(
LoadQueueSize
+
1
).
W
))
val
allowEnqueue
=
RegInit
(
true
.
B
)
val
enqPtr
=
enqPtrExt
(
0
).
value
...
...
src/main/scala/xiangshan/mem/lsqueue/StoreQueue.scala
浏览文件 @
4fcc0784
...
...
@@ -58,7 +58,7 @@ class StoreQueue extends XSModule with HasDCacheParameters with HasCircularQueue
require
(
StoreQueueSize
>
RenameWidth
)
val
enqPtrExt
=
RegInit
(
VecInit
((
0
until
RenameWidth
).
map
(
_
.
U
.
asTypeOf
(
new
SqPtr
))))
val
deqPtrExt
=
RegInit
(
VecInit
((
0
until
StorePipelineWidth
).
map
(
_
.
U
.
asTypeOf
(
new
SqPtr
))))
val
validCounter
=
RegInit
(
0.
U
(
log2Ceil
(
LoadQueueSize
).
W
))
val
validCounter
=
RegInit
(
0.
U
(
log2Ceil
(
LoadQueueSize
+
1
).
W
))
val
allowEnqueue
=
RegInit
(
true
.
B
)
val
enqPtr
=
enqPtrExt
(
0
).
value
...
...
src/test/csrc/emu.cpp
浏览文件 @
4fcc0784
...
...
@@ -367,12 +367,12 @@ uint64_t Emulator::execute(uint64_t max_cycle, uint64_t max_instr) {
}
#ifdef VM_SAVABLE
static
int
snapshot_count
=
0
;
if
(
args
.
enable_snapshot
&&
trapCode
!=
STATE_GOODTRAP
&&
t
-
lasttime_snapshot
>
6
000
*
SNAPSHOT_INTERVAL
)
{
if
(
args
.
enable_snapshot
&&
trapCode
!=
STATE_GOODTRAP
&&
t
-
lasttime_snapshot
>
1
000
*
SNAPSHOT_INTERVAL
)
{
// save snapshot every 60s
time_t
now
=
time
(
NULL
);
snapshot_save
(
snapshot_filename
(
now
));
lasttime_snapshot
=
t
;
// dump
snapshot to file every 10 minute
s
// dump
one snapshot to file every 60 snapshot
s
snapshot_count
++
;
if
(
snapshot_count
==
60
)
{
snapshot_slot
[
0
].
save
();
...
...
@@ -422,6 +422,7 @@ inline char* Emulator::waveform_filename(time_t t) {
static
char
buf
[
1024
];
char
*
p
=
timestamp_filename
(
t
,
buf
);
strcpy
(
p
,
".vcd"
);
printf
(
"dump wave to %s...
\n
"
,
buf
);
return
buf
;
}
...
...
src/test/csrc/emu.h
浏览文件 @
4fcc0784
...
...
@@ -4,7 +4,7 @@
#include <verilated_vcd_c.h> // Trace file format header
#define DIFFTEST_WIDTH 6
#define SNAPSHOT_INTERVAL
1
0 // unit: second
#define SNAPSHOT_INTERVAL
6
0 // unit: second
struct
EmuArgs
{
uint32_t
seed
;
...
...
src/test/scala/xiangshan/frontend/IFUTest.scala
浏览文件 @
4fcc0784
package
xiangshan.frontend
//
package xiangshan.frontend
import
chisel3._
import
chiseltest._
import
org.scalatest.flatspec.AnyFlatSpec
import
org.scalatest.matchers.must.Matchers
//
import chisel3._
//
import chiseltest._
//
import org.scalatest.flatspec.AnyFlatSpec
//
import org.scalatest.matchers.must.Matchers
class
IFUTest
extends
AnyFlatSpec
with
ChiselScalatestTester
with
Matchers
{
behavior
of
"IFU Test"
//
class IFUTest extends AnyFlatSpec with ChiselScalatestTester with Matchers {
//
behavior of "IFU Test"
it
should
"test IFU pipeline"
in
{
test
(
new
IFU
)
{
c
=>
//-----------------
//Cycle 0
//-----------------
//c.io.icacheReq.ready.poke(true.B)
c
.
io
.
icacheReq
.
ready
.
poke
(
false
.
B
)
c
.
io
.
fetchPacket
.
ready
.
poke
(
true
.
B
)
c
.
clock
.
step
()
//-----------------
//Cycle 1
//-----------------
c
.
clock
.
step
()
c
.
clock
.
step
()
c
.
clock
.
step
()
//-----------------
// Cycle 2
//-----------------
c
.
io
.
icacheReq
.
ready
.
poke
(
true
.
B
)
c
.
clock
.
step
()
//-----------------
// Cycle 3
//-----------------
c
.
clock
.
step
()
//-----------------
// Cycle 4
//-----------------
c
.
io
.
icacheResp
.
valid
.
poke
(
true
.
B
)
c
.
clock
.
step
()
//-----------------
// Cycle 5
//-----------------
c
.
io
.
redirect
.
valid
.
poke
(
true
.
B
)
c
.
io
.
redirect
.
bits
.
poke
(
"h80002800"
.
U
)
c
.
clock
.
step
()
//-----------------
// Cycle 6
//-----------------
c
.
io
.
redirect
.
valid
.
poke
(
false
.
B
)
c
.
clock
.
step
()
//-----------------
// Cycle 7
//-----------------
c
.
clock
.
step
()
}
}
}
//
it should "test IFU pipeline" in {
//
test(new IFU) { c =>
//
//-----------------
//
//Cycle 0
//
//-----------------
//
//c.io.icacheReq.ready.poke(true.B)
//
c.io.icacheReq.ready.poke(false.B)
//
c.io.fetchPacket.ready.poke(true.B)
//
c.clock.step()
//
//-----------------
//
//Cycle 1
//
//-----------------
//
c.clock.step()
//
c.clock.step()
//
c.clock.step()
//
//-----------------
//
// Cycle 2
//
//-----------------
//
c.io.icacheReq.ready.poke(true.B)
//
c.clock.step()
//
//-----------------
//
// Cycle 3
//
//-----------------
//
c.clock.step()
//
//-----------------
//
// Cycle 4
//
//-----------------
//
c.io.icacheResp.valid.poke(true.B)
//
c.clock.step()
//
//-----------------
//
// Cycle 5
//
//-----------------
//
c.io.redirect.valid.poke(true.B)
//
c.io.redirect.bits.poke("h80002800".U)
//
c.clock.step()
//
//-----------------
//
// Cycle 6
//
//-----------------
//
c.io.redirect.valid.poke(false.B)
//
c.clock.step()
//
//-----------------
//
// Cycle 7
//
//-----------------
//
c.clock.step()
//
}
//
}
//
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录