Contributed by: <
HotMercury
(p76111741) >, <freshLiver
(P76114016) >, <tinhanho
(P76121364) >
HotMercury
(p76111741)>freshLiver
(P76114016)>
tinhanho
(P76121364)>
--recurse-submodules
option, or you will fail to build it!!!git submodule update --init --recursive
after cloning because there are external sources called VSRTL, ELFIO and libelfin.docker run --rm --name=ripes -it --entrypoint=/bin/bash ripes:latest
useradd -m user
(assume the host $UID is also 1000)docker commit ripes cafinal:user
docker stop ripes
docker run --rm --name=ripes -u 1000 -it -v $YOUR_RIPES_DIR:/ripes cafinal:user
:
cd /ripes/build/test
./tst_assembler && ./tst_expreval && ./tst_riscv
--recurse-submodules
are recommended.git submodule update --init --recursive
sudo aqt install-qt linux desktop 6.5.0 gcc_64 -m qtcharts
sudo make install
./Ripes
sudo ./Ripes
if we want to test system call which likes file open and so on.We found that when using the open
system call, the mode permission settings need to be specified in octal form. According to the Linux man page, when we set 00200
, it means the user has write permission. However, when attempting a write operation, we observed a write error. The issue appears to be related to Ripes interpreting the set number as decimal instead of the octal mode.
Read write test in Ripes
When using the open(fd, flags)
syscall implemented by Ripes, it cannot write data to the file if we set the a1
(flags
) to 1 (O_WRONLY
) or 2 (O_RDWR
). Instead, we need to set it to 3.
However, the QFile::write
doesn't require the flag to be 3, we can write a simple program to test:
Then, create a CMakeLists.txt
file with the following lines:
And build and test the simple program with:
The created file yoyo
should contain the string aaaa
.
If we check the write
syscall implementation, we could find that it explicitly requires O_WRONLY | O_RDWR
:
Why the implementation require the flags to be O_WRONLY | O_RDWR
???
As the issue desciption said, in the latest Ripes, most of the I-type instructions will check whether the given immediate part can fit in the instruction limitation.
For example, the addi
instruction will ensure the given immediate is less than 13 bits, as the following image shows:
However, the lui
instruction's immediate part should only accept an immediate value that could fit in 20 bits, but current version doesn't handle this limitation correctly, as shown in the above image.
To find the problem, I use std::cout
to dump the width
in the checkFitsInWidth
function, which is for checking the immediate range:
Then, rebuild the Ripes and test it with the following codes:
And found that the width
of lui
instruction is misconfigured as 32, instead of 20:
And this is origin from the the U-type instruction implementation, we can see that the width
is configured as 32:
By changing the width to 20, the bit range checking now works as expected, on the lui
and auipc
now:
Currently I only use the provided testing utility for checking my changes didn't break it:
However, since I'm not that familiar with C++, I'm not sure whether I understood the bit range checking process correctly. If there is any problem in my changes, please me know.
In file Ripes/src/syscall/ripes_syscall.h
:
The word explicitely
should be explicitly
.
Hi @matsievskiysv
According to the code in systemio.h
at line 80,
which defines the value of the flags used by the open
syscall.
However, it cannot write data into the file correctly, if we only set O_WRONLY
when calling open
syscall.
Looking at the conditional statements at lines 408-409, it input fd
and 3
as parameter
and then checks for the existence of the "write" flag using fdInUse at line193
it requires the flag to be 0x3
to be true. Therefore, setting it only to O_WRONLY
may result in a write failure.
Hi @mortbopet
Ideally, writing to a file should be allowed as long as it satisfies either O_WRONLY
or O_RDWR
. However, here it is specified that both conditions must be met for a write operation to proceed. I would like to inquire whether the author has any specific reason for this additional requirement.
Hi @mortbopet,
I find that contributing to the cache replacement policy is a good way to work with. The replacement policy which possesses from now on are Random and LRU. Intuitively, FIFO can be added and it has been done through few lines of code. What I am doing now is trying to add policy like least frequently used. However, I cannot find out where the code is to expand the slot just like LRU doing. For example, when 2 ways cache used, you click Repl. policy LRU and the LRU slots show up.
I guess that it might be the UI problem but I do not know how to modify it. Could you help me?
About Issue #334.
I think that we could add a FIFO mechanism to the cache policy.
A new replacement policy, FIFO (First In, First Out), has been added to the existing enumeration. Additionally, a new counter has been introduced in the cachesim.h file. This counter plays a crucial role in cyclically determining which cache entry should be evicted.
The code below handles the eviction process based on the FIFO replacement policy.
Thank you for looking into adding a new cache replacement policy!
A few comments:
As per the coding style currently used in Ripes, member variables should be prefixed with m_ and not with the class name.
I think the counter should be named better, e.g. fifoIndexCounter.
(optional) do you think there is an accompanying visualization to this? i.e., for LRU, we also show the LRU bits. Could one imagine a similar column which indicates what way in the cache line is currently up for eviction as per. FIFO?
Thanks for the reply. I had already renamed the counter and tried my best to follow the coding style. If there are any problem still, please let me know.
As for third comments, I rework all the framework to implement visualizing the FIFO bit. Now, there is no need about fifoIndexCounter. Instead, boolean fifoflag is presented. This flag is set under two circumstances,
When this flag is set, we shall add 1 to fifo bits if entry is valid. In this way, we'll find that when fifo bits equal to the way of the cache, that entry should be evicted.
Furthermore, the undo part needs to be taken into consideration as well. If the policy is FIFO, we set the fifoflag again and we need to restore the oldway.
Demo video here,
https://github.com/mortbopet/Ripes/assets/67796326/b67aab30-17ee-4b3c-8fa7-ba56f905a282
Demo video demostrates the assembly code below,
Full code is in the branch FIFO of my fork,
https://github.com/tinhanho/Ripes/commit/6772d1abec6a6bff5e0e40374fd5922c67e3a427
Let me know if there are any problems of my think and implement.
The base of the instruction is the class InstructionBase
defined in the src/isa/instruction.h
.
And this class is inheritted by the Instruction
structure:
This structure implement the assemble
and disassemble
functions used for assembling/disassembling the instructions at runtime. But
Since the real instruction implementations are defined in the src/isa/rv_[icm]_ext.h
files, take I-type for example:
The I-type instructions are defined under the namespace TypeI
in src/isa/rv_i_ext.h
:
RV_Instruction
is a simple wrapper of Instruction
These instructions have the common format imm[11:0] | rs1 | funct3 | rd | opcode (0b0010011)
where the fields imm
(RegRd
), rs1
(RegRs1
), rd
(ImmCommon12
) are available when parsing the assembly codes. So, in the definition of Addi
, the most important thing is to define the funct3
(Funct3::ADDI
).
The JALR
instruction:
Note that an exception is the JALR
instruction, its opcode is 0b1100111
and thus inherit the RV_Instruction
directly.
During runtime, the instructions will be initialized by using the enableInstructions
function, defined in the src/isa/instruction.h
, to add the instructions into the InstrVec m_instructions
of the RV_ISAInfoBase
class:
Then, later will use the function setInstructions
to initialize the map InstrMap m_instructionMap
of all the previously defined instructions:
Then, the function Assembler::assemble
, defined in src/assembler/assembler.h
, will be the entry point for assembling the instructions, during runtime:
Then, in this function, the function pass2
is used for translating the instruction:
And the function assembleInstruction
will be used for assembling and checking an instruction, if any error is found, the error message will be pushed to the error list and highlighted on the editor:
It will first check whether the opcode (instruction name) is legal. If true, then it will retrieve the instruction implementation from Assembler::m_instructionMap
, and call the assemble
function implemented by that instruction.
As explained above, because the instruction implementations are inheritted from the Instruction
structure, if the instruction didn't override the assemble
function, the default assemble
implementation should be defined by the Instruction
structure:
When the implementation being called, if the apply
function didn't be overridden by the instruction implementation, it will first use that provided by struct OpPartBase
defined in src/isa/instruction.h
, to combine the fields into a complete instruction:
When syscall->execute()
is executed, the corresponding syscall implementation will be called. For the open
syscall:
And then call the SystemIO::openFile(...)
:
This function first allocate a fd
for the specified file by using FileIOData::nowOpening()
, which saves the parameter flags
into an map called fileFlags[i]
:
Then, it will try to open the file with FileIOData::openFilestream()
:
In this function, we can find that the open
syscall relies on the QFile::open()
provided by Qt. The given flags
will be converted to the Qt defined flags:
Constant | Value |
---|---|
QIODeviceBase::NotOpen | 0x0000 |
QIODeviceBase::ReadOnly | 0x0001 |
QIODeviceBase::WriteOnly | 0x0002 |
QIODeviceBase::ReadWrite | ReadOnly | WriteOnly |
QIODeviceBase::Append | 0x0004 |
QIODeviceBase::Truncate | 0x0008 |
QIODeviceBase::Text | 0x0010 |
QIODeviceBase::Unbuffered | 0x0020 |
QIODeviceBase::NewOnly | 0x0040 |
QIODeviceBase::ExistingOnly | 0x0080 |
Note that the Ripes defined flags listed above are NOT identical to the (standard? linux defined?) flags:
In src/cachesim/cachesim.h
, there is a enumeration ReplPolicy
which is used for listing the available cache replacement policies:
And in the src/cachesim/cachesim.h
, the function CacheSim::access
is the main function for accessing the cache. And in this function, if cache miss is happened, the function CacheSim::evictAndUpdate
will be used for selecting the victim:
The function CacheSim::evictAndUpdate
will first determine which way the victim should be selected from, by using the function CacheSim::locateEvictionWay
. And after the victim way is determined, it will update the cache line flags:
However, we can find that the victim cache line is not updated directly, it's substituted with a new cache line instead. The reasons are:
Therefore, the function will also record the updates into transaction
and return the evicted cache line.
As mentioned before, the function CacheSim::locateEvictionWay
is used for selecting the victim way.
In this function, it first check the replacement policy. It randomly select a way if ReplPolicy::Random
is in used:
Otherwise, the LRU will be performed to find the first invalid cache line. If all the cache lines are valid, it will select the LRU line: